001package com.thetransactioncompany.util;
002
003
004import java.net.MalformedURLException;
005import java.net.URI;
006import java.net.URISyntaxException;
007import java.net.URL;
008import java.util.Properties;
009
010
011/**
012 * Provides typed retrieval of {@link java.util.Properties} as {@code boolean}, 
013 * {@code int}, {@code long}, {@code float}, {@code double}, 
014 * {@link java.lang.String}, {@link java.net.URL}, {@link java.net.URI} or
015 * {@code enum} values.
016 *
017 * @author Vladimir Dzhuvinov
018 */
019public class PropertyRetriever {
020
021        
022        /** 
023         * The properties to parse.
024         */
025        private final Properties props;
026
027
028        /**
029         * {@code true} if system property override has been enabled,
030         * {@code false} if it's disabled.
031         */
032        private final boolean enableSysPropOverride;
033
034
035        /**
036         * Creates a new retriever for the specified properties. System
037         * property override is disabled.
038         *
039         * @param props The properties. Must not be {@code null}.
040         */
041        public PropertyRetriever(final Properties props) {
042        
043                this(props, false);
044        }
045
046
047        /**
048         * Creates a new retriever for the specified properties.
049         *
050         * @param props                 The properties. Must not be
051         *                              {@code null}.
052         * @param enableSysPropOverride If {@code true} if system properties
053         *                              with the same names are present they
054         *                              will override the specified properties,
055         *                              {@code false} to disable this
056         *                              behaviour.
057         */
058        public PropertyRetriever(final Properties props, final boolean enableSysPropOverride) {
059
060                this.props = new Properties();
061                this.props.putAll(props);
062
063                this.enableSysPropOverride = enableSysPropOverride;
064                if (enableSysPropOverride) {
065                        // Overwrite
066                        this.props.putAll(System.getProperties());
067                }
068        }
069
070
071        /**
072         * Gets the system property override setting.
073         *
074         * @return {@code true} if system property override has been enabled,
075         *         {@code false} if it's disabled.
076         */
077        public boolean systemPropertyOverrideIsEnabled() {
078
079                return enableSysPropOverride;
080        }
081        
082        
083        /**
084         * Retrieves a boolean value.
085         *
086         * @param key The property name.
087         *
088         * @return The property as a boolean value.
089         *
090         * @throws PropertyParseException On a missing or invalid property.
091         */
092        public boolean getBoolean(final String key)
093                throws PropertyParseException {
094        
095                String value = props.getProperty(key);
096                
097                if (value == null)
098                        throw new PropertyParseException("Missing property", key);
099                
100                if (value.equalsIgnoreCase("true"))
101                        return true;
102                
103                else if (value.equalsIgnoreCase("false"))
104                        return false;
105                
106                else
107                        throw new PropertyParseException("Invalid boolean property", key, value);
108        }
109        
110        
111        /**
112         * Retrieves an optional boolean value.
113         *
114         * @param key The property name.
115         * @param def The default value if the property value is undefined or
116         *            empty.
117         *
118         * @return The property as a boolean.
119         *
120         * @throws PropertyParseException On an invalid property.
121         */
122        public boolean getOptBoolean(final String key, final boolean def)
123                throws PropertyParseException {
124        
125                String value = props.getProperty(key);
126                
127                if (value == null || value.trim().isEmpty())
128                        return def;
129                
130                if (value.equalsIgnoreCase("true"))
131                        return true;
132                
133                if (value.equalsIgnoreCase("false"))
134                        return false;
135                
136                throw new PropertyParseException("Invalid boolean property", key, value);
137        }
138        
139        
140        /**
141         * Retrieves an integer value.
142         *
143         * @param key The property name.
144         *
145         * @return The property as an integer.
146         *
147         * @throws PropertyParseException On a missing or invalid property.
148         */
149        public int getInt(final String key)
150                throws PropertyParseException {
151        
152                String value = props.getProperty(key);
153                
154                if (value == null)
155                        throw new PropertyParseException("Missing property", key);
156                
157                try {
158                        return Integer.parseInt(value);
159                
160                } catch (NumberFormatException e) {
161
162                        throw new PropertyParseException("Invalid int property", key, value);
163                }
164        }
165        
166        
167        /**
168         * Retrieves an optional integer value.
169         *
170         * @param key The property name.
171         * @param def The default value if the property value is undefined or
172         *            empty.
173         *
174         * @return The property as an integer.
175         *
176         * @throws PropertyParseException On an invalid property.
177         */
178        public int getOptInt(final String key, final int def)
179                throws PropertyParseException {
180        
181                String value = props.getProperty(key);
182                
183                if (value == null || value.trim().isEmpty())
184                        return def;
185                
186                try {
187                        return Integer.parseInt(value);
188                
189                } catch (NumberFormatException e) {
190
191                        throw new PropertyParseException("Invalid int property", key);
192                }
193        }
194        
195        
196        /**
197         * Retrieves a long value.
198         *
199         * @param key The property name.
200         *
201         * @return The property as a long.
202         *
203         * @throws PropertyParseException On a missing or invalid property.
204         */
205        public long getLong(final String key)
206                throws PropertyParseException {
207        
208                String value = props.getProperty(key);
209                
210                if (value == null)
211                        throw new PropertyParseException("Missing property", key);
212                
213                try {
214                        return Long.parseLong(value);
215                
216                } catch (NumberFormatException e) {
217
218                        throw new PropertyParseException("Invalid long property", key, value);
219                }
220        }
221        
222        
223        /**
224         * Retrieves an optional long value.
225         *
226         * @param key The property name.
227         * @param def The default value if the property value is undefined or
228         *            empty.
229         *
230         * @return The property as a long.
231         *
232         * @throws PropertyParseException On an invalid property.
233         */
234        public long getOptLong(final String key, final long def)
235                throws PropertyParseException {
236        
237                String value = props.getProperty(key);
238                
239                if (value == null || value.trim().isEmpty())
240                        return def;
241                
242                try {
243                        return Long.parseLong(value);
244                
245                } catch (NumberFormatException e) {
246
247                        throw new PropertyParseException("Invalid long property", key, value);
248                }
249        }
250        
251        
252        /**
253         * Retrieves a float value.
254         *
255         * @param key The property name.
256         *
257         * @return The property as a float.
258         *
259         * @throws PropertyParseException On a missing or invalid property.
260         */
261        public float getFloat(final String key)
262                throws PropertyParseException {
263        
264                String value = props.getProperty(key);
265                
266                if (value == null)
267                        throw new PropertyParseException("Missing property", key);
268                
269                try {
270                        return Float.parseFloat(value);
271                
272                } catch (NumberFormatException e) {
273
274                        throw new PropertyParseException("Invalid float property", key, value);
275                }
276        }
277        
278        
279        /**
280         * Retrieves an optional float value.
281         *
282         * @param key The property name.
283         * @param def The default value if the property value is undefined or
284         *            empty.
285         *
286         * @return The property as a float.
287         *
288         * @throws PropertyParseException On an invalid property.
289         */
290        public float getOptFloat(final String key, final float def)
291                throws PropertyParseException {
292        
293                String value = props.getProperty(key);
294                
295                if (value == null || value.trim().isEmpty())
296                        return def;
297                
298                try {
299                        return Float.parseFloat(value);
300                
301                } catch (NumberFormatException e) {
302
303                        throw new PropertyParseException("Invalid float property", key, value);
304                }
305        }
306        
307        
308        /**
309         * Retrieves a double value.
310         *
311         * @param key The property name.
312         *
313         * @return The property as a double.
314         *
315         * @throws PropertyParseException On a missing or invalid property.
316         */
317        public double getDouble(final String key)
318                throws PropertyParseException {
319        
320                String value = props.getProperty(key);
321                
322                if (value == null)
323                        throw new PropertyParseException("Missing property", key);
324                
325                try {
326                        return Double.parseDouble(value);
327                
328                } catch (NumberFormatException e) {
329
330                        throw new PropertyParseException("Invalid double property", key, value);
331                }
332        }
333        
334        
335        /**
336         * Retrieves an optional double value.
337         *
338         * @param key The property name.
339         * @param def The default value if the property value is undefined or
340         *            empty.
341         *
342         * @return The property as a double.
343         *
344         * @throws PropertyParseException On an invalid property.
345         */
346        public double getOptDouble(final String key, final double def)
347                throws PropertyParseException {
348        
349                String value = props.getProperty(key);
350                
351                if (value == null || value.trim().isEmpty())
352                        return def;
353                
354                try {
355                        return Double.parseDouble(value);
356                
357                } catch (NumberFormatException e) {
358
359                        throw new PropertyParseException("Invalid double property", key, value);
360                }
361        }
362        
363        
364        /**
365         * Retrieves a string value.
366         *
367         * @param key The property name.
368         *
369         * @return The property as a string.
370         *
371         * @throws PropertyParseException On a missing or invalid property.
372         */
373        public String getString(final String key)
374                throws PropertyParseException {
375        
376                String value = props.getProperty(key);
377                
378                if (value == null)
379                        throw new PropertyParseException("Missing property", key);
380                
381                return value;
382        }
383        
384        
385        /**
386         * Retrieves an optional string value.
387         *
388         * @param key The property name.
389         * @param def The default value if the property value is undefined or
390         *            empty.
391         *
392         * @return The property as a string.
393         *
394         * @throws PropertyParseException On an invalid property.
395         */
396        public String getOptString(final String key, final String def)
397                throws PropertyParseException {
398        
399                String value = props.getProperty(key);
400                
401                if (value == null || value.trim().isEmpty())
402                        return def;
403                
404                return value;
405        }
406        
407        
408        /**
409         * Retrieves an enumerated string value. String case is ignored during
410         * comparison.
411         *
412         * @param key   The property name.
413         * @param enums A string array defining the acceptable values.
414         *
415         * @return The property as a string.
416         *
417         * @throws PropertyParseException On a missing or invalid property.
418         */
419        public String getEnumString(final String key, final String[] enums)
420                throws PropertyParseException {
421        
422                String value = props.getProperty(key);
423                
424                if (value == null)
425                        throw new PropertyParseException("Missing property", key);
426                
427                for (String en: enums) {
428                        
429                        if (en.equalsIgnoreCase(value))
430                                return value;
431                }
432                        
433                throw new PropertyParseException("Invalid enum string property", key, value);
434        }
435        
436        
437        /**
438         * Retrieves an enumerated string value. String case is ignored during
439         * comparison.
440         *
441         * @param key   The property name.
442         * @param enums A string array defining the acceptable values.
443         * @param def   The default value if the property value is undefined or
444         *              empty.
445         *
446         * @return The property as a string.
447         *
448         * @throws PropertyParseException On an invalid property.
449         */
450        public String getOptEnumString(final String key, final String[] enums, final String def)
451                throws PropertyParseException {
452        
453                String value = props.getProperty(key);
454                
455                if (value == null || value.trim().isEmpty())
456                        return def;
457                
458                for (String en: enums) {
459                        
460                        if (en.equalsIgnoreCase(value))
461                                return value;
462                }
463                        
464                throw new PropertyParseException("Invalid enum string property", key, value);
465        }
466        
467        
468        /**
469         * Retrieves an enumerated constant. String case is ignored during
470         * comparison.
471         *
472         * @param key       The property name.
473         * @param enumClass The enumeration class specifying the acceptable
474         *                  values.
475         *
476         * @return The matching enumerated constant.
477         *
478         * @throws PropertyParseException On a missing or invalid property.
479         */
480        public <T extends Enum<T>> T getEnum(final String key, final Class<T> enumClass)
481                throws PropertyParseException {
482                
483                String value = props.getProperty(key);
484                
485                if (value == null)
486                        throw new PropertyParseException("Missing property", key);
487                        
488                for (T en: enumClass.getEnumConstants()) {
489                
490                        if (en.toString().equalsIgnoreCase(value))
491                                return en;
492                }
493                
494                // No match? -> raise exception
495                throw new PropertyParseException("Invalid enum property", key, value);
496        }
497        
498        
499        /**
500         * Retrieves an optional enumerated constant. String case is ignored
501         * during comparison.
502         *
503         * @param key       The property name.
504         * @param enumClass The enumeration class specifying the acceptable
505         *                  values.
506         * @param def       The default value if the property value is 
507         *                  undefined or empty.
508         *
509         * @return The matching enumerated constant.
510         *
511         * @throws PropertyParseException On a missing or invalid property.
512         */
513        public <T extends Enum<T>> T getOptEnum(final String key, final Class<T> enumClass, final T def)
514                throws PropertyParseException {
515                
516                String value = props.getProperty(key);
517                
518                if (value == null || value.trim().isEmpty())
519                        return def;
520                        
521                for (T en: enumClass.getEnumConstants()) {
522                
523                        if (en.toString().equalsIgnoreCase(value))
524                                return en;
525                }
526                
527                // No match? -> raise exception
528                throw new PropertyParseException("Invalid enum property", key, value);
529        }
530        
531        
532        /**
533         * Retrieves a URI value.
534         *
535         * @param key The property name.
536         *
537         * @return The property as a URI.
538         *
539         * @throws PropertyParseException On a missing or invalid property.
540         */
541        public URI getURI(final String key)
542                throws PropertyParseException {
543                
544                String value = props.getProperty(key);
545                
546                if (value == null)
547                        throw new PropertyParseException("Missing property", key);
548                
549                try {
550                        return new URI(value);
551                        
552                } catch (URISyntaxException e) {
553                        
554                        throw new PropertyParseException("Invalid URI property: " + e.getMessage(), key, value);
555                }
556        }
557        
558        
559        /**
560         * Retrieves an optional URI value.
561         *
562         * @param key The property name.
563         * @param def The default value if the property value is undefined or
564         *            empty.
565         *
566         * @return The property as a URL.
567         *
568         * @throws PropertyParseException On an invalid property.
569         */
570        public URI getOptURI(final String key, final URI def)
571                throws PropertyParseException {
572                
573                String value = props.getProperty(key);
574                
575                if (value == null || value.trim().isEmpty())
576                        return def;
577                
578                try {
579                        return new URI(value);
580                        
581                } catch (URISyntaxException e) {
582                        
583                        throw new PropertyParseException("Invalid URI property: " + e.getMessage(), key, value);
584                }
585        }
586
587
588        /**
589         * Retrieves a URL value.
590         *
591         * @param key The property name.
592         *
593         * @return The property as a URL.
594         *
595         * @throws PropertyParseException On a missing or invalid property.
596         */
597        public URL getURL(final String key)
598                throws PropertyParseException {
599        
600                String value = props.getProperty(key);
601                
602                if (value == null)
603                        throw new PropertyParseException("Missing property", key);
604                
605                try {
606                        return new URL(value);
607
608                } catch (MalformedURLException e) {
609
610                        throw new PropertyParseException("Invalid URL property: " + e.getMessage(), key, value);
611                }
612        }
613        
614        
615        /**
616         * Retrieves an optional URL value.
617         *
618         * @param key The property name.
619         * @param def The default value if the property value is undefined or
620         *            empty.
621         *
622         * @return The property as a URL.
623         *
624         * @throws PropertyParseException On an invalid property.
625         */
626        public URL getOptURL(final String key, final URL def)
627                throws PropertyParseException {
628        
629                String value = props.getProperty(key);
630                
631                if (value == null || value.trim().isEmpty())
632                        return def;
633                
634                try {
635                        return new URL(value);
636
637                } catch (MalformedURLException e) {
638
639                        throw new PropertyParseException("Invalid URL property: " + e.getMessage(), key, value);
640                }
641        }
642}