001    /*
002     *  Copyright 2001-2005 Stephen Colebourne
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     *      http://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    package org.joda.time.convert;
017    
018    import org.joda.time.JodaTimePermission;
019    
020    /**
021     * ConverterManager controls the date and time converters.
022     * <p>
023     * This class enables additional conversion classes to be added via
024     * {@link #addInstantConverter(InstantConverter)}, which may replace an
025     * existing converter. Similar methods exist for duration, time period and
026     * interval converters.
027     * <p>
028     * This class is threadsafe, so adding/removing converters can be done at any
029     * time. Updating the set of convertors is relatively expensive, and so should
030     * not be performed often.
031     * <p>
032     * The default instant converters are:
033     * <ul>
034     * <li>ReadableInstant
035     * <li>String
036     * <li>Calendar
037     * <li>Date (includes sql package subclasses)
038     * <li>Long (milliseconds)
039     * <li>null (now)
040     * </ul>
041     * 
042     * The default partial converters are:
043     * <ul>
044     * <li>ReadablePartial
045     * <li>ReadableInstant
046     * <li>String
047     * <li>Calendar
048     * <li>Date (includes sql package subclasses)
049     * <li>Long (milliseconds)
050     * <li>null (now)
051     * </ul>
052     * 
053     * The default duration converters are:
054     * <ul>
055     * <li>ReadableDuration
056     * <li>ReadableInterval
057     * <li>String
058     * <li>Long (milliseconds)
059     * <li>null (zero ms)
060     * </ul>
061     *
062     * The default time period converters are:
063     * <ul>
064     * <li>ReadablePeriod
065     * <li>ReadableInterval
066     * <li>String
067     * <li>null (zero)
068     * </ul>
069     * 
070     * The default interval converters are:
071     * <ul>
072     * <li>ReadableInterval
073     * <li>String
074     * <li>null (zero-length from now to now)
075     * </ul>
076     *
077     * @author Stephen Colebourne
078     * @author Brian S O'Neill
079     * @since 1.0
080     */
081    public final class ConverterManager {
082    
083        /**
084         * Singleton instance, lazily loaded to avoid class loading.
085         */
086        private static ConverterManager INSTANCE;
087    
088        public static ConverterManager getInstance() {
089            if (INSTANCE == null) {
090                INSTANCE = new ConverterManager();
091            }
092            return INSTANCE;
093        }
094        
095        private ConverterSet iInstantConverters;
096        private ConverterSet iPartialConverters;
097        private ConverterSet iDurationConverters;
098        private ConverterSet iPeriodConverters;
099        private ConverterSet iIntervalConverters;
100        
101        /**
102         * Restricted constructor.
103         */
104        protected ConverterManager() {
105            super();
106    
107            iInstantConverters = new ConverterSet(new Converter[] {
108                ReadableInstantConverter.INSTANCE,
109                StringConverter.INSTANCE,
110                CalendarConverter.INSTANCE,
111                DateConverter.INSTANCE,
112                LongConverter.INSTANCE,
113                NullConverter.INSTANCE,
114            });
115    
116            iPartialConverters = new ConverterSet(new Converter[] {
117                ReadablePartialConverter.INSTANCE,
118                ReadableInstantConverter.INSTANCE,
119                StringConverter.INSTANCE,
120                CalendarConverter.INSTANCE,
121                DateConverter.INSTANCE,
122                LongConverter.INSTANCE,
123                NullConverter.INSTANCE,
124            });
125    
126            iDurationConverters = new ConverterSet(new Converter[] {
127                ReadableDurationConverter.INSTANCE,
128                ReadableIntervalConverter.INSTANCE,
129                StringConverter.INSTANCE,
130                LongConverter.INSTANCE,
131                NullConverter.INSTANCE,
132            });
133    
134            iPeriodConverters = new ConverterSet(new Converter[] {
135                ReadableDurationConverter.INSTANCE,
136                ReadablePeriodConverter.INSTANCE,
137                ReadableIntervalConverter.INSTANCE,
138                StringConverter.INSTANCE,
139                NullConverter.INSTANCE,
140            });
141    
142            iIntervalConverters = new ConverterSet(new Converter[] {
143                ReadableIntervalConverter.INSTANCE,
144                StringConverter.INSTANCE,
145                NullConverter.INSTANCE,
146            });
147        }
148    
149        //-----------------------------------------------------------------------
150        /**
151         * Gets the best converter for the object specified.
152         * 
153         * @param object  the object to convert
154         * @return the converter to use
155         * @throws IllegalArgumentException if no suitable converter
156         * @throws IllegalStateException if multiple converters match the type
157         * equally well
158         */
159        public InstantConverter getInstantConverter(Object object) {
160            InstantConverter converter =
161                (InstantConverter)iInstantConverters.select(object == null ? null : object.getClass());
162            if (converter != null) {
163                return converter;
164            }
165            throw new IllegalArgumentException("No instant converter found for type: " +
166                (object == null ? "null" : object.getClass().getName()));
167        }
168        
169        //-----------------------------------------------------------------------
170        /**
171         * Gets a copy of the set of converters.
172         * 
173         * @return the converters, a copy of the real data, never null
174         */
175        public InstantConverter[] getInstantConverters() {
176            ConverterSet set = iInstantConverters;
177            InstantConverter[] converters = new InstantConverter[set.size()];
178            set.copyInto(converters);
179            return converters;
180        }
181        
182        /**
183         * Adds a converter to the set of converters. If a matching converter is
184         * already in the set, the given converter replaces it. If the converter is
185         * exactly the same as one already in the set, no changes are made.
186         * <p>
187         * The order in which converters are added is not relevent. The best
188         * converter is selected by examining the object hierarchy.
189         * 
190         * @param converter  the converter to add, null ignored
191         * @return replaced converter, or null
192         */
193        public InstantConverter addInstantConverter(InstantConverter converter)
194                throws SecurityException {
195            
196            checkAlterInstantConverters();
197            if (converter == null) {
198                return null;
199            }
200            InstantConverter[] removed = new InstantConverter[1];
201            iInstantConverters = iInstantConverters.add(converter, removed);
202            return removed[0];
203        }
204        
205        /**
206         * Removes a converter from the set of converters. If the converter was
207         * not in the set, no changes are made.
208         * 
209         * @param converter  the converter to remove, null ignored
210         * @return replaced converter, or null
211         */
212        public InstantConverter removeInstantConverter(InstantConverter converter)
213                throws SecurityException {
214            
215            checkAlterInstantConverters();
216            if (converter == null) {
217                return null;
218            }
219            InstantConverter[] removed = new InstantConverter[1];
220            iInstantConverters = iInstantConverters.remove(converter, removed);
221            return removed[0];
222        }
223        
224        /**
225         * Checks whether the user has permission 'ConverterManager.alterInstantConverters'.
226         * 
227         * @throws SecurityException if the user does not have the permission
228         */
229        private void checkAlterInstantConverters() throws SecurityException {
230            SecurityManager sm = System.getSecurityManager();
231            if (sm != null) {
232                sm.checkPermission(new JodaTimePermission("ConverterManager.alterInstantConverters"));
233            }
234        }
235    
236        //-----------------------------------------------------------------------
237        /**
238         * Gets the best converter for the object specified.
239         * 
240         * @param object  the object to convert
241         * @return the converter to use
242         * @throws IllegalArgumentException if no suitable converter
243         * @throws IllegalStateException if multiple converters match the type
244         * equally well
245         */
246        public PartialConverter getPartialConverter(Object object) {
247            PartialConverter converter =
248                (PartialConverter)iPartialConverters.select(object == null ? null : object.getClass());
249            if (converter != null) {
250                return converter;
251            }
252            throw new IllegalArgumentException("No partial converter found for type: " +
253                (object == null ? "null" : object.getClass().getName()));
254        }
255        
256        //-----------------------------------------------------------------------
257        /**
258         * Gets a copy of the set of converters.
259         * 
260         * @return the converters, a copy of the real data, never null
261         */
262        public PartialConverter[] getPartialConverters() {
263            ConverterSet set = iPartialConverters;
264            PartialConverter[] converters = new PartialConverter[set.size()];
265            set.copyInto(converters);
266            return converters;
267        }
268        
269        /**
270         * Adds a converter to the set of converters. If a matching converter is
271         * already in the set, the given converter replaces it. If the converter is
272         * exactly the same as one already in the set, no changes are made.
273         * <p>
274         * The order in which converters are added is not relevent. The best
275         * converter is selected by examining the object hierarchy.
276         * 
277         * @param converter  the converter to add, null ignored
278         * @return replaced converter, or null
279         */
280        public PartialConverter addPartialConverter(PartialConverter converter)
281                throws SecurityException {
282            
283            checkAlterPartialConverters();
284            if (converter == null) {
285                return null;
286            }
287            PartialConverter[] removed = new PartialConverter[1];
288            iPartialConverters = iPartialConverters.add(converter, removed);
289            return removed[0];
290        }
291        
292        /**
293         * Removes a converter from the set of converters. If the converter was
294         * not in the set, no changes are made.
295         * 
296         * @param converter  the converter to remove, null ignored
297         * @return replaced converter, or null
298         */
299        public PartialConverter removePartialConverter(PartialConverter converter)
300                throws SecurityException {
301            
302            checkAlterPartialConverters();
303            if (converter == null) {
304                return null;
305            }
306            PartialConverter[] removed = new PartialConverter[1];
307            iPartialConverters = iPartialConverters.remove(converter, removed);
308            return removed[0];
309        }
310        
311        /**
312         * Checks whether the user has permission 'ConverterManager.alterPartialConverters'.
313         * 
314         * @throws SecurityException if the user does not have the permission
315         */
316        private void checkAlterPartialConverters() throws SecurityException {
317            SecurityManager sm = System.getSecurityManager();
318            if (sm != null) {
319                sm.checkPermission(new JodaTimePermission("ConverterManager.alterPartialConverters"));
320            }
321        }
322    
323        //-----------------------------------------------------------------------
324        /**
325         * Gets the best converter for the object specified.
326         * 
327         * @param object  the object to convert
328         * @return the converter to use
329         * @throws IllegalArgumentException if no suitable converter
330         * @throws IllegalStateException if multiple converters match the type
331         * equally well
332         */
333        public DurationConverter getDurationConverter(Object object) {
334            DurationConverter converter =
335                (DurationConverter)iDurationConverters.select(object == null ? null : object.getClass());
336            if (converter != null) {
337                return converter;
338            }
339            throw new IllegalArgumentException("No duration converter found for type: " +
340                (object == null ? "null" : object.getClass().getName()));
341        }
342        
343        //-----------------------------------------------------------------------
344        /**
345         * Gets a copy of the list of converters.
346         * 
347         * @return the converters, a copy of the real data, never null
348         */
349        public DurationConverter[] getDurationConverters() {
350            ConverterSet set = iDurationConverters;
351            DurationConverter[] converters = new DurationConverter[set.size()];
352            set.copyInto(converters);
353            return converters;
354        }
355        
356        /**
357         * Adds a converter to the set of converters. If a matching converter is
358         * already in the set, the given converter replaces it. If the converter is
359         * exactly the same as one already in the set, no changes are made.
360         * <p>
361         * The order in which converters are added is not relevent. The best
362         * converter is selected by examining the object hierarchy.
363         * 
364         * @param converter  the converter to add, null ignored
365         * @return replaced converter, or null
366         */
367        public DurationConverter addDurationConverter(DurationConverter converter)
368                throws SecurityException {
369            
370            checkAlterDurationConverters();
371            if (converter == null) {
372                return null;
373            }
374            DurationConverter[] removed = new DurationConverter[1];
375            iDurationConverters = iDurationConverters.add(converter, removed);
376            return removed[0];
377        }
378        
379        /**
380         * Removes a converter from the set of converters. If the converter was
381         * not in the set, no changes are made.
382         * 
383         * @param converter  the converter to remove, null ignored
384         * @return replaced converter, or null
385         */
386        public DurationConverter removeDurationConverter(DurationConverter converter)
387                throws SecurityException {
388            
389            checkAlterDurationConverters();
390            if (converter == null) {
391                return null;
392            }
393            DurationConverter[] removed = new DurationConverter[1];
394            iDurationConverters = iDurationConverters.remove(converter, removed);
395            return removed[0];
396        }
397        
398        /**
399         * Checks whether the user has permission 'ConverterManager.alterDurationConverters'.
400         * 
401         * @throws SecurityException if the user does not have the permission
402         */
403        private void checkAlterDurationConverters() throws SecurityException {
404            SecurityManager sm = System.getSecurityManager();
405            if (sm != null) {
406                sm.checkPermission(new JodaTimePermission("ConverterManager.alterDurationConverters"));
407            }
408        }
409    
410        //-----------------------------------------------------------------------
411        /**
412         * Gets the best converter for the object specified.
413         * 
414         * @param object  the object to convert
415         * @return the converter to use
416         * @throws IllegalArgumentException if no suitable converter
417         * @throws IllegalStateException if multiple converters match the type
418         * equally well
419         */
420        public PeriodConverter getPeriodConverter(Object object) {
421            PeriodConverter converter =
422                (PeriodConverter)iPeriodConverters.select(object == null ? null : object.getClass());
423            if (converter != null) {
424                return converter;
425            }
426            throw new IllegalArgumentException("No period converter found for type: " +
427                (object == null ? "null" : object.getClass().getName()));
428        }
429        
430        //-----------------------------------------------------------------------
431        /**
432         * Gets a copy of the list of converters.
433         * 
434         * @return the converters, a copy of the real data, never null
435         */
436        public PeriodConverter[] getPeriodConverters() {
437            ConverterSet set = iPeriodConverters;
438            PeriodConverter[] converters = new PeriodConverter[set.size()];
439            set.copyInto(converters);
440            return converters;
441        }
442        
443        /**
444         * Adds a converter to the set of converters. If a matching converter is
445         * already in the set, the given converter replaces it. If the converter is
446         * exactly the same as one already in the set, no changes are made.
447         * <p>
448         * The order in which converters are added is not relevent. The best
449         * converter is selected by examining the object hierarchy.
450         * 
451         * @param converter  the converter to add, null ignored
452         * @return replaced converter, or null
453         */
454        public PeriodConverter addPeriodConverter(PeriodConverter converter)
455                throws SecurityException {
456            
457            checkAlterPeriodConverters();
458            if (converter == null) {
459                return null;
460            }
461            PeriodConverter[] removed = new PeriodConverter[1];
462            iPeriodConverters = iPeriodConverters.add(converter, removed);
463            return removed[0];
464        }
465        
466        /**
467         * Removes a converter from the set of converters. If the converter was
468         * not in the set, no changes are made.
469         * 
470         * @param converter  the converter to remove, null ignored
471         * @return replaced converter, or null
472         */
473        public PeriodConverter removePeriodConverter(PeriodConverter converter)
474                throws SecurityException {
475            
476            checkAlterPeriodConverters();
477            if (converter == null) {
478                return null;
479            }
480            PeriodConverter[] removed = new PeriodConverter[1];
481            iPeriodConverters = iPeriodConverters.remove(converter, removed);
482            return removed[0];
483        }
484        
485        /**
486         * Checks whether the user has permission 'ConverterManager.alterPeriodConverters'.
487         * 
488         * @throws SecurityException if the user does not have the permission
489         */
490        private void checkAlterPeriodConverters() throws SecurityException {
491            SecurityManager sm = System.getSecurityManager();
492            if (sm != null) {
493                sm.checkPermission(new JodaTimePermission("ConverterManager.alterPeriodConverters"));
494            }
495        }
496    
497        //-----------------------------------------------------------------------
498        /**
499         * Gets the best converter for the object specified.
500         * 
501         * @param object  the object to convert
502         * @return the converter to use
503         * @throws IllegalArgumentException if no suitable converter
504         * @throws IllegalStateException if multiple converters match the type
505         * equally well
506         */
507        public IntervalConverter getIntervalConverter(Object object) {
508            IntervalConverter converter =
509                (IntervalConverter)iIntervalConverters.select(object == null ? null : object.getClass());
510            if (converter != null) {
511                return converter;
512            }
513            throw new IllegalArgumentException("No interval converter found for type: " +
514                (object == null ? "null" : object.getClass().getName()));
515        }
516        
517        //-----------------------------------------------------------------------
518        /**
519         * Gets a copy of the list of converters.
520         * 
521         * @return the converters, a copy of the real data, never null
522         */
523        public IntervalConverter[] getIntervalConverters() {
524            ConverterSet set = iIntervalConverters;
525            IntervalConverter[] converters = new IntervalConverter[set.size()];
526            set.copyInto(converters);
527            return converters;
528        }
529        
530        /**
531         * Adds a converter to the set of converters. If a matching converter is
532         * already in the set, the given converter replaces it. If the converter is
533         * exactly the same as one already in the set, no changes are made.
534         * <p>
535         * The order in which converters are added is not relevent. The best
536         * converter is selected by examining the object hierarchy.
537         * 
538         * @param converter  the converter to add, null ignored
539         * @return replaced converter, or null
540         */
541        public IntervalConverter addIntervalConverter(IntervalConverter converter) 
542                throws SecurityException {
543            
544            checkAlterIntervalConverters();
545            if (converter == null) {
546                return null;
547            }
548            IntervalConverter[] removed = new IntervalConverter[1];
549            iIntervalConverters = iIntervalConverters.add(converter, removed);
550            return removed[0];
551        }
552        
553        /**
554         * Removes a converter from the set of converters. If the converter was
555         * not in the set, no changes are made.
556         * 
557         * @param converter  the converter to remove, null ignored
558         * @return replaced converter, or null
559         */
560        public IntervalConverter removeIntervalConverter(IntervalConverter converter)
561                throws SecurityException {
562            
563            checkAlterIntervalConverters();
564            if (converter == null) {
565                return null;
566            }
567            IntervalConverter[] removed = new IntervalConverter[1];
568            iIntervalConverters = iIntervalConverters.remove(converter, removed);
569            return removed[0];
570        }
571        
572        /**
573         * Checks whether the user has permission 'ConverterManager.alterIntervalConverters'.
574         * 
575         * @throws SecurityException if the user does not have the permission
576         */
577        private void checkAlterIntervalConverters() throws SecurityException {
578            SecurityManager sm = System.getSecurityManager();
579            if (sm != null) {
580                sm.checkPermission(new JodaTimePermission("ConverterManager.alterIntervalConverters"));
581            }
582        }
583    
584        //-----------------------------------------------------------------------
585        /**
586         * Gets a debug representation of the object.
587         */
588        public String toString() {
589            return "ConverterManager[" +
590                iInstantConverters.size() + " instant," +
591                iPartialConverters.size() + " partial," +
592                iDurationConverters.size() + " duration," +
593                iPeriodConverters.size() + " period," +
594                iIntervalConverters.size() + " interval]";
595        }
596    
597    }