1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html#License
3 /*
4 ******************************************************************************
5 * Copyright (C) 2007-2009, International Business Machines Corporation and   *
6 * others. All Rights Reserved.                                               *
7 ******************************************************************************
8 */
9 
10 package com.ibm.icu.impl.duration;
11 
12 import java.util.Locale;
13 import java.util.TimeZone;
14 
15 /**
16  * Abstract factory object used to create DurationFormatters.
17  * DurationFormatters are immutable once created.
18  * <p>
19  * Setters on the factory mutate the factory and return it,
20  * for chaining.
21  * <p>
22  * Subclasses override getFormatter to return a custom
23  * DurationFormatter.
24  */
25 class BasicDurationFormatterFactory implements DurationFormatterFactory {
26   private BasicPeriodFormatterService ps;
27   private PeriodFormatter formatter;
28   private PeriodBuilder builder;
29   private DateFormatter fallback;
30   private long fallbackLimit;
31   private String localeName;
32   private TimeZone timeZone;
33   private BasicDurationFormatter f; // cache
34 
35   /**
36    * Create a default formatter for the current locale and time zone.
37    */
BasicDurationFormatterFactory(BasicPeriodFormatterService ps)38   BasicDurationFormatterFactory(BasicPeriodFormatterService ps) {
39     this.ps = ps;
40     this.localeName = Locale.getDefault().toString();
41     this.timeZone = TimeZone.getDefault();
42   }
43 
44   /**
45    * Set the period formatter used by the factory.  New formatters created
46    * with this factory will use the given period formatter.
47    *
48    * @return this BasicDurationFormatterFactory
49    */
50   @Override
setPeriodFormatter( PeriodFormatter formatter)51   public DurationFormatterFactory setPeriodFormatter(
52       PeriodFormatter formatter) {
53     if (formatter != this.formatter) {
54       this.formatter = formatter;
55       reset();
56     }
57     return this;
58   }
59 
60   /**
61    * Set the builder used by the factory.  New formatters created
62    * with this factory will use the given locale.
63    *
64    * @param builder the builder to use
65    * @return this BasicDurationFormatterFactory
66    */
67   @Override
setPeriodBuilder(PeriodBuilder builder)68   public DurationFormatterFactory setPeriodBuilder(PeriodBuilder builder) {
69     if (builder != this.builder) {
70       this.builder = builder;
71       reset();
72     }
73     return this;
74   }
75 
76   /**
77    * Set a fallback formatter for durations over a given limit.
78    *
79    * @param fallback the fallback formatter to use, or null
80    * @return this BasicDurationFormatterFactory
81    */
82   @Override
setFallback(DateFormatter fallback)83   public DurationFormatterFactory setFallback(DateFormatter fallback) {
84     boolean doReset = fallback == null
85         ? this.fallback != null
86         : !fallback.equals(this.fallback);
87     if (doReset) {
88       this.fallback = fallback;
89       reset();
90     }
91     return this;
92   }
93 
94   /**
95    * Set a fallback limit for durations over a given limit.
96    *
97    * @param fallbackLimit the fallback limit to use, or 0 if none is desired.
98    * @return this BasicDurationFormatterFactory
99    */
100   @Override
setFallbackLimit(long fallbackLimit)101   public DurationFormatterFactory setFallbackLimit(long fallbackLimit) {
102     if (fallbackLimit < 0) {
103       fallbackLimit = 0;
104     }
105     if (fallbackLimit != this.fallbackLimit) {
106       this.fallbackLimit = fallbackLimit;
107       reset();
108     }
109     return this;
110   }
111 
112   /**
113    * Set the name of the locale that will be used when
114    * creating new formatters.
115    *
116    * @param localeName the name of the Locale
117    * @return this BasicDurationFormatterFactory
118    */
119   @Override
setLocale(String localeName)120   public DurationFormatterFactory setLocale(String localeName) {
121     if (!localeName.equals(this.localeName)) {
122       this.localeName = localeName;
123       if (builder != null) {
124           builder = builder.withLocale(localeName);
125       }
126       if (formatter != null) {
127           formatter = formatter.withLocale(localeName);
128       }
129       reset();
130     }
131     return this;
132   }
133 
134   /**
135    * Set the name of the locale that will be used when
136    * creating new formatters.
137    *
138    * @param timeZone The time zone to use.
139    * @return this BasicDurationFormatterFactory
140    */
141   @Override
setTimeZone(TimeZone timeZone)142   public DurationFormatterFactory setTimeZone(TimeZone timeZone) {
143     if (!timeZone.equals(this.timeZone)) {
144       this.timeZone = timeZone;
145       if (builder != null) {
146           builder = builder.withTimeZone(timeZone);
147       }
148       reset();
149     }
150     return this;
151   }
152 
153   /**
154    * Return a formatter based on this factory's current settings.
155    *
156    * @return a BasicDurationFormatter
157    */
158   @Override
getFormatter()159   public DurationFormatter getFormatter() {
160     if (f == null) {
161       if (fallback != null) {
162         fallback = fallback.withLocale(localeName).withTimeZone(timeZone);
163       }
164       formatter = getPeriodFormatter();
165       builder = getPeriodBuilder();
166 
167       f = createFormatter();
168     }
169     return f;
170   }
171 
172   /**
173    * Return the current period formatter.
174    *
175    * @return the current period formatter
176    */
getPeriodFormatter()177   public PeriodFormatter getPeriodFormatter() {
178     if (formatter == null) {
179       formatter = ps.newPeriodFormatterFactory()
180           .setLocale(localeName)
181           .getFormatter();
182     }
183     return formatter;
184   }
185 
186   /**
187    * Return the current builder.
188    *
189    * @return the current builder
190    */
getPeriodBuilder()191   public PeriodBuilder getPeriodBuilder() {
192     if (builder == null) {
193       builder = ps.newPeriodBuilderFactory()
194           .setLocale(localeName)
195           .setTimeZone(timeZone)
196           .getSingleUnitBuilder();
197     }
198     return builder;
199   }
200 
201   /**
202    * Return the current fallback formatter.
203    *
204    * @return the fallback formatter, or null if there is no fallback
205    * formatter
206    */
getFallback()207   public DateFormatter getFallback() {
208     return fallback;
209   }
210 
211   /**
212    * Return the current fallback formatter limit
213    *
214    * @return the limit, or 0 if there is no fallback.
215    */
getFallbackLimit()216   public long getFallbackLimit() {
217     return fallback == null ? 0 : fallbackLimit;
218   }
219 
220   /**
221    * Return the current locale name.
222    *
223    * @return the current locale name
224    */
getLocaleName()225   public String getLocaleName() {
226     return localeName;
227   }
228 
229   /**
230    * Return the current locale name.
231    *
232    * @return the current locale name
233    */
getTimeZone()234   public TimeZone getTimeZone() {
235     return timeZone;
236   }
237 
238   /**
239    * Create the formatter.  All local fields are already initialized.
240    */
createFormatter()241   protected BasicDurationFormatter createFormatter() {
242     return new BasicDurationFormatter(formatter, builder, fallback,
243                                       fallbackLimit, localeName,
244                                       timeZone);
245   }
246 
247   /**
248    * Clear the cached formatter.  Subclasses must call this if their
249    * state has changed. This is automatically invoked by setBuilder,
250    * setFormatter, setFallback, setLocaleName, and setTimeZone
251    */
reset()252   protected void reset() {
253     f = null;
254   }
255 }
256