1 | /* |
2 | * Copyright 2001-2005 Stephen Colebourne |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | package org.joda.time.convert; |
17 | |
18 | import org.joda.time.JodaTimePermission; |
19 | |
20 | /** |
21 | * ConverterManager controls the date and time converters. |
22 | * <p> |
23 | * This class enables additional conversion classes to be added via |
24 | * {@link #addInstantConverter(InstantConverter)}, which may replace an |
25 | * existing converter. Similar methods exist for duration, time period and |
26 | * interval converters. |
27 | * <p> |
28 | * This class is threadsafe, so adding/removing converters can be done at any |
29 | * time. Updating the set of convertors is relatively expensive, and so should |
30 | * not be performed often. |
31 | * <p> |
32 | * The default instant converters are: |
33 | * <ul> |
34 | * <li>ReadableInstant |
35 | * <li>String |
36 | * <li>Calendar |
37 | * <li>Date (includes sql package subclasses) |
38 | * <li>Long (milliseconds) |
39 | * <li>null (now) |
40 | * </ul> |
41 | * |
42 | * The default partial converters are: |
43 | * <ul> |
44 | * <li>ReadablePartial |
45 | * <li>ReadableInstant |
46 | * <li>String |
47 | * <li>Calendar |
48 | * <li>Date (includes sql package subclasses) |
49 | * <li>Long (milliseconds) |
50 | * <li>null (now) |
51 | * </ul> |
52 | * |
53 | * The default duration converters are: |
54 | * <ul> |
55 | * <li>ReadableDuration |
56 | * <li>ReadableInterval |
57 | * <li>String |
58 | * <li>Long (milliseconds) |
59 | * <li>null (zero ms) |
60 | * </ul> |
61 | * |
62 | * The default time period converters are: |
63 | * <ul> |
64 | * <li>ReadablePeriod |
65 | * <li>ReadableInterval |
66 | * <li>String |
67 | * <li>null (zero) |
68 | * </ul> |
69 | * |
70 | * The default interval converters are: |
71 | * <ul> |
72 | * <li>ReadableInterval |
73 | * <li>String |
74 | * <li>null (zero-length from now to now) |
75 | * </ul> |
76 | * |
77 | * @author Stephen Colebourne |
78 | * @author Brian S O'Neill |
79 | * @since 1.0 |
80 | */ |
81 | public final class ConverterManager { |
82 | |
83 | /** |
84 | * Singleton instance, lazily loaded to avoid class loading. |
85 | */ |
86 | private static ConverterManager INSTANCE; |
87 | |
88 | public static ConverterManager getInstance() { |
89 | if (INSTANCE == null) { |
90 | INSTANCE = new ConverterManager(); |
91 | } |
92 | return INSTANCE; |
93 | } |
94 | |
95 | private ConverterSet iInstantConverters; |
96 | private ConverterSet iPartialConverters; |
97 | private ConverterSet iDurationConverters; |
98 | private ConverterSet iPeriodConverters; |
99 | 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 | } |