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-2010, 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 
14 import com.ibm.icu.impl.duration.impl.DataRecord.ECountVariant;
15 import com.ibm.icu.impl.duration.impl.DataRecord.ESeparatorVariant;
16 import com.ibm.icu.impl.duration.impl.DataRecord.EUnitVariant;
17 import com.ibm.icu.impl.duration.impl.PeriodFormatterData;
18 import com.ibm.icu.impl.duration.impl.PeriodFormatterDataService;
19 
20 /**
21  * An implementation of PeriodFormatterFactory that provides customization of
22  * formatting behavior. Instances of this factory are created by
23  * BasicPeriodFormatterService.
24  *
25  * The settings on BasicPeriodFormatterFactory are:
26  * <ul>
27  *
28  * <li><b>setDisplayLimit</b> controls whether phrases like 'more than'
29  * or 'less than' will be displayed when the Period has a defined
30  * limit.  Default is to display them.</li>
31  *
32  * <li><b>setDisplayPastFuture</b> controls whether phrases like 'ago'
33  * or 'from now' will be displayed to indicate past or future
34  * time. Default is to display them.</li>
35  *
36  * <li><b>setSeparatorVariant</b> controls how separators (between
37  * count and period, and multiple periods) will be displayed, when
38  * appropriate for the language. Default is to use full
39  * separators.</li>
40  *
41  * <li><b>setUnitVariant</b> controls which of various types of
42  * unit names to use.  PLURALIZED indicates that full names will be
43  * used.  MEDIUM indicates that medium-length (usually 2-3 character)
44  * names will be used.  SHORT indicates that short (usually single
45  * character) names will be used.  If there is no localization data
46  * available for either the SHORT or MEDIUM names, the other will be
47  * used, if neither is available, the PLURALIZED names will be used.
48  * Default is PLURALIZED.</li>
49  *
50  * <li><b>setCountVariant</b> controls how the count for the smallest
51  * unit will be formatted: either as an integer, a fraction to the
52  * smallest half, or as a decimal with 1, 2, or 3 decimal points.</li>
53  * Counts for higher units will be formatted as integers.
54  *
55  * </ul>
56  */
57 public class BasicPeriodFormatterFactory implements PeriodFormatterFactory {
58   private final PeriodFormatterDataService ds;
59   private PeriodFormatterData data;
60   private Customizations customizations;
61   private boolean customizationsInUse;
62   private String localeName;
63 
64   // package-only constructor
BasicPeriodFormatterFactory(PeriodFormatterDataService ds)65   BasicPeriodFormatterFactory(PeriodFormatterDataService ds) {
66     this.ds = ds;
67     this.customizations = new Customizations();
68     this.localeName = Locale.getDefault().toString();
69   }
70 
71   /**
72    * Return the default rdf factory as a BasicPeriodFormatterFactory.
73    *
74    * @return a default BasicPeriodFormatterFactory
75    */
getDefault()76   public static BasicPeriodFormatterFactory getDefault() {
77       return (BasicPeriodFormatterFactory)
78         BasicPeriodFormatterService.getInstance().newPeriodFormatterFactory();
79   }
80 
81   /**
82    * Set the locale for this factory.
83    */
84   @Override
setLocale(String localeName)85   public PeriodFormatterFactory setLocale(String localeName) {
86     data = null;
87     this.localeName = localeName;
88     return this;
89   }
90 
91   /**
92    * Set whether limits will be displayed.
93    *
94    * @param display true if limits will be displayed
95    * @return this PeriodFormatterFactory
96    */
97   @Override
setDisplayLimit(boolean display)98   public PeriodFormatterFactory setDisplayLimit(boolean display) {
99     updateCustomizations().displayLimit = display;
100     return this;
101   }
102 
103   /**
104    * Return true if limits will be displayed.
105    *
106    * @return true if limits will be displayed
107    */
getDisplayLimit()108   public boolean getDisplayLimit() {
109     return customizations.displayLimit;
110   }
111 
112   /**
113    * Set whether past and future will be displayed.
114    *
115    * @param display true if past and future will be displayed
116    * @return this PeriodFormatterFactory
117    */
118   @Override
setDisplayPastFuture(boolean display)119   public PeriodFormatterFactory setDisplayPastFuture(boolean display) {
120     updateCustomizations().displayDirection = display;
121     return this;
122   }
123 
124   /**
125    * Return true if past and future will be displayed.
126    *
127    * @return true if past and future will be displayed
128    */
getDisplayPastFuture()129   public boolean getDisplayPastFuture() {
130     return customizations.displayDirection;
131   }
132 
133   /**
134    * Set how separators will be displayed.
135    *
136    * @param variant the variant indicating separators will be displayed
137    * @return this PeriodFormatterFactory
138    */
139   @Override
setSeparatorVariant(int variant)140   public PeriodFormatterFactory setSeparatorVariant(int variant) {
141     updateCustomizations().separatorVariant = (byte) variant;
142     return this;
143   }
144 
145   /**
146    * Return the variant indicating how separators will be displayed.
147    *
148    * @return the variant
149    */
getSeparatorVariant()150   public int getSeparatorVariant() {
151     return customizations.separatorVariant;
152   }
153 
154   /**
155    * Set the variant of the time unit names to use.
156    *
157    * @param variant the variant to use
158    * @return this PeriodFormatterFactory
159    */
160   @Override
setUnitVariant(int variant)161   public PeriodFormatterFactory setUnitVariant(int variant) {
162     updateCustomizations().unitVariant = (byte) variant;
163     return this;
164   }
165 
166   /**
167    * Return the unit variant.
168    *
169    * @return the unit variant
170    */
getUnitVariant()171   public int getUnitVariant() {
172     return customizations.unitVariant;
173   }
174 
175   /**
176    * Set the variant of the count to use.
177    *
178    * @param variant the variant to use
179    * @return this PeriodFormatterFactory
180    */
181   @Override
setCountVariant(int variant)182   public PeriodFormatterFactory setCountVariant(int variant) {
183     updateCustomizations().countVariant = (byte) variant;
184     return this;
185   }
186 
187   /**
188    * Return the count variant.
189    *
190    * @return the count variant
191    */
getCountVariant()192   public int getCountVariant() {
193     return customizations.countVariant;
194   }
195 
196   @Override
getFormatter()197   public PeriodFormatter getFormatter() {
198     customizationsInUse = true;
199     return new BasicPeriodFormatter(this, localeName, getData(),
200                                     customizations);
201   }
202 
updateCustomizations()203   private Customizations updateCustomizations() {
204     if (customizationsInUse) {
205       customizations = customizations.copy();
206       customizationsInUse = false;
207     }
208     return customizations;
209   }
210 
211   // package access only
getData()212   PeriodFormatterData getData() {
213     if (data == null) {
214       data = ds.get(localeName);
215     }
216     return data;
217   }
218 
219   // package access for use by BasicPeriodFormatter
getData(String locName)220   PeriodFormatterData getData(String locName) {
221     return ds.get(locName);
222   }
223 
224   // package access for use by BasicPeriodFormatter
225   static class Customizations {
226     boolean displayLimit = true;
227     boolean displayDirection = true;
228     byte separatorVariant = ESeparatorVariant.FULL;
229     byte unitVariant = EUnitVariant.PLURALIZED;
230     byte countVariant = ECountVariant.INTEGER;
231 
copy()232     public Customizations copy() {
233         Customizations result = new Customizations();
234         result.displayLimit = displayLimit;
235         result.displayDirection = displayDirection;
236         result.separatorVariant = separatorVariant;
237         result.unitVariant = unitVariant;
238         result.countVariant = countVariant;
239         return result;
240     }
241   }
242 }
243