]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_4_2-src/main/classes/core/src/com/ibm/icu/impl/duration/BasicPeriodBuilderFactory.java
go
[Dictionary.git] / jars / icu4j-4_4_2-src / main / classes / core / src / com / ibm / icu / impl / duration / BasicPeriodBuilderFactory.java
1 /*\r
2 ******************************************************************************\r
3 * Copyright (C) 2007-2009, International Business Machines Corporation and   *\r
4 * others. All Rights Reserved.                                               *\r
5 ******************************************************************************\r
6 */\r
7 \r
8 package com.ibm.icu.impl.duration;\r
9 \r
10 import com.ibm.icu.impl.duration.impl.DataRecord;\r
11 import com.ibm.icu.impl.duration.impl.PeriodFormatterData;\r
12 import com.ibm.icu.impl.duration.impl.PeriodFormatterDataService;\r
13 \r
14 import java.util.TimeZone;\r
15 \r
16 /**\r
17  * Default implementation of PeriodBuilderFactory.  This creates builders that\r
18  * use approximate durations.\r
19  */\r
20 class BasicPeriodBuilderFactory implements PeriodBuilderFactory {\r
21   private PeriodFormatterDataService ds;\r
22   private Settings settings;\r
23   \r
24   private static final short allBits = 0xff;\r
25   \r
26   BasicPeriodBuilderFactory(PeriodFormatterDataService ds) {\r
27     this.ds = ds;\r
28     this.settings = new Settings();\r
29   }\r
30 \r
31   static long approximateDurationOf(TimeUnit unit) {\r
32     return TimeUnit.approxDurations[unit.ordinal];\r
33   }\r
34 \r
35   class Settings {\r
36     boolean inUse;\r
37     short uset = allBits;\r
38     TimeUnit maxUnit = TimeUnit.YEAR;\r
39     TimeUnit minUnit = TimeUnit.MILLISECOND;\r
40     int maxLimit;\r
41     int minLimit;\r
42     boolean allowZero = true;\r
43     boolean weeksAloneOnly;\r
44     boolean allowMillis = true;\r
45 \r
46     Settings setUnits(int uset) {\r
47       if (this.uset == uset) {\r
48         return this;\r
49       }\r
50       Settings result = inUse ? copy() : this;\r
51         \r
52       result.uset = (short)uset;\r
53 \r
54       if ((uset & allBits) == allBits) {\r
55         result.uset = allBits;\r
56         result.maxUnit = TimeUnit.YEAR;\r
57         result.minUnit = TimeUnit.MILLISECOND;\r
58       } else {\r
59         int lastUnit = -1;\r
60         for (int i = 0; i < TimeUnit.units.length; ++i) {\r
61           if (0 != (uset & (1 << i))) {\r
62             if (lastUnit == -1) {\r
63               result.maxUnit = TimeUnit.units[i];\r
64             }\r
65             lastUnit = i;\r
66           }\r
67         }\r
68         if (lastUnit == -1) { \r
69             // currently empty, but this might be transient so no fail\r
70             result.minUnit = result.maxUnit = null;\r
71         } else {\r
72             result.minUnit = TimeUnit.units[lastUnit];\r
73         }\r
74       }\r
75 \r
76       return result;\r
77     }\r
78 \r
79     short effectiveSet() {\r
80       if (allowMillis) {\r
81         return uset;\r
82       }\r
83       return (short)(uset & ~(1 << TimeUnit.MILLISECOND.ordinal));\r
84     }\r
85     \r
86     TimeUnit effectiveMinUnit() {\r
87         if (allowMillis || minUnit != TimeUnit.MILLISECOND) {\r
88             return minUnit;\r
89         }\r
90         // -1 to skip millisecond\r
91         for (int i = TimeUnit.units.length - 1; --i >= 0;) {\r
92             if (0 != (uset & (1 << i))) {\r
93                 return TimeUnit.units[i];\r
94             }\r
95         }\r
96         return TimeUnit.SECOND; // default for pathological case\r
97     }\r
98     \r
99     Settings setMaxLimit(float maxLimit) {\r
100       int val = maxLimit <= 0 ? 0 : (int)(maxLimit*1000);\r
101       if (maxLimit == val) {\r
102         return this;\r
103       }\r
104       Settings result = inUse ? copy() : this;\r
105       result.maxLimit = val;\r
106       return result;\r
107     }\r
108 \r
109     Settings setMinLimit(float minLimit) {\r
110       int val = minLimit <= 0 ? 0 : (int)(minLimit*1000);\r
111       if (minLimit == val) {\r
112         return this;\r
113       }\r
114       Settings result = inUse ? copy() : this;\r
115       result.minLimit = val;\r
116       return result;\r
117     }\r
118 \r
119     Settings setAllowZero(boolean allow) {\r
120       if (this.allowZero == allow) {\r
121         return this;\r
122       }\r
123       Settings result = inUse ? copy() : this;\r
124       result.allowZero = allow;\r
125       return result;\r
126     }\r
127 \r
128     Settings setWeeksAloneOnly(boolean weeksAlone) {\r
129       if (this.weeksAloneOnly == weeksAlone) {\r
130         return this;\r
131       }\r
132       Settings result = inUse ? copy() : this;\r
133       result.weeksAloneOnly = weeksAlone;\r
134       return result;\r
135     }\r
136 \r
137     Settings setAllowMilliseconds(boolean allowMillis) {\r
138       if (this.allowMillis == allowMillis) {\r
139         return this;\r
140       }\r
141       Settings result = inUse ? copy() : this;\r
142       result.allowMillis = allowMillis;\r
143       return result;\r
144     }\r
145 \r
146     Settings setLocale(String localeName) {\r
147       PeriodFormatterData data = ds.get(localeName);\r
148       return this\r
149         .setAllowZero(data.allowZero())\r
150         .setWeeksAloneOnly(data.weeksAloneOnly())\r
151         .setAllowMilliseconds(data.useMilliseconds() != DataRecord.EMilliSupport.NO);  \r
152     }\r
153 \r
154     Settings setInUse() {\r
155       inUse = true;\r
156       return this;\r
157     }\r
158 \r
159     Period createLimited(long duration, boolean inPast) {\r
160       if (maxLimit > 0) {\r
161           long maxUnitDuration = approximateDurationOf(maxUnit);\r
162           if (duration * 1000 > maxLimit * maxUnitDuration) {\r
163               return Period.moreThan(maxLimit/1000f, maxUnit).inPast(inPast);\r
164           }\r
165       }\r
166       \r
167       if (minLimit > 0) {\r
168           TimeUnit emu = effectiveMinUnit();\r
169           long emud = approximateDurationOf(emu);\r
170           long eml = (emu == minUnit) ? minLimit :\r
171               Math.max(1000, (approximateDurationOf(minUnit) * minLimit) / emud);\r
172           if (duration * 1000 < eml * emud) {\r
173               return Period.lessThan(eml/1000f, emu).inPast(inPast);\r
174           }\r
175       }\r
176       return null;\r
177     }\r
178 \r
179     public Settings copy() {\r
180         Settings result = new Settings();\r
181         result.inUse = inUse;\r
182         result.uset = uset;\r
183         result.maxUnit = maxUnit;\r
184         result.minUnit = minUnit;\r
185         result.maxLimit = maxLimit;\r
186         result.minLimit = minLimit;\r
187         result.allowZero = allowZero;\r
188         result.weeksAloneOnly = weeksAloneOnly;\r
189         result.allowMillis = allowMillis;\r
190         return result;\r
191     }\r
192   }\r
193 \r
194   public PeriodBuilderFactory setAvailableUnitRange(TimeUnit minUnit,\r
195                                                     TimeUnit maxUnit) {\r
196     int uset = 0;\r
197     for (int i = maxUnit.ordinal; i <= minUnit.ordinal; ++i) {\r
198         uset |= 1 << i;\r
199     }\r
200     if (uset == 0) {\r
201         throw new IllegalArgumentException("range " + minUnit + " to " + maxUnit + " is empty");\r
202     }\r
203     settings = settings.setUnits(uset);\r
204     return this;\r
205   }\r
206 \r
207   public PeriodBuilderFactory setUnitIsAvailable(TimeUnit unit, \r
208                                                       boolean available) {\r
209     int uset = settings.uset;\r
210     if (available) {\r
211       uset |= 1 << unit.ordinal;\r
212     } else {\r
213       uset &= ~(1 << unit.ordinal);\r
214     }\r
215     settings = settings.setUnits(uset);\r
216     return this;\r
217   }\r
218 \r
219   public PeriodBuilderFactory setMaxLimit(float maxLimit) {\r
220     settings = settings.setMaxLimit(maxLimit);\r
221     return this;\r
222   }\r
223 \r
224   public PeriodBuilderFactory setMinLimit(float minLimit) {\r
225     settings = settings.setMinLimit(minLimit);\r
226     return this;\r
227   }\r
228 \r
229   public PeriodBuilderFactory setAllowZero(boolean allow) {\r
230     settings = settings.setAllowZero(allow);\r
231     return this;\r
232   }\r
233 \r
234   public PeriodBuilderFactory setWeeksAloneOnly(boolean aloneOnly) {\r
235     settings = settings.setWeeksAloneOnly(aloneOnly);\r
236     return this;\r
237   }\r
238 \r
239   public PeriodBuilderFactory setAllowMilliseconds(boolean allow) {\r
240     settings = settings.setAllowMilliseconds(allow);\r
241     return this;\r
242   }\r
243 \r
244   public PeriodBuilderFactory setLocale(String localeName) {\r
245     settings = settings.setLocale(localeName);\r
246     return this;\r
247   }\r
248   \r
249   public PeriodBuilderFactory setTimeZone(TimeZone timeZone) {\r
250       // ignore this\r
251       return this;\r
252   }\r
253 \r
254   private Settings getSettings() {\r
255     if (settings.effectiveSet() == 0) {\r
256       return null;\r
257     }\r
258     return settings.setInUse();\r
259   }\r
260 \r
261   /**\r
262    * Return a builder that represents relative time in terms of the single\r
263    * given TimeUnit\r
264    *\r
265    * @param unit the single TimeUnit with which to represent times\r
266    * @return a builder\r
267    */\r
268   public PeriodBuilder getFixedUnitBuilder(TimeUnit unit) {\r
269     return FixedUnitBuilder.get(unit, getSettings());\r
270   }\r
271 \r
272   /**\r
273    * Return a builder that represents relative time in terms of the\r
274    * largest period less than or equal to the duration.\r
275    *\r
276    * @return a builder\r
277    */\r
278   public PeriodBuilder getSingleUnitBuilder() {\r
279     return SingleUnitBuilder.get(getSettings());\r
280   }\r
281 \r
282   /**\r
283    * Return a builder that formats the largest one or two periods,\r
284    * Starting with the largest period less than or equal to the duration.\r
285    * It formats two periods if the first period has a count &lt; 2\r
286    * and the next period has a count &gt;= 1.\r
287    *\r
288    * @return a builder\r
289    */\r
290   public PeriodBuilder getOneOrTwoUnitBuilder() {\r
291     return OneOrTwoUnitBuilder.get(getSettings());\r
292   }\r
293 \r
294   /**\r
295    * Return a builder that formats the given number of periods,\r
296    * starting with the largest period less than or equal to the\r
297    * duration.\r
298    *\r
299    * @return a builder\r
300    */\r
301   public PeriodBuilder getMultiUnitBuilder(int periodCount) {\r
302     return MultiUnitBuilder.get(periodCount, getSettings());\r
303   }\r
304 }\r
305 \r
306 abstract class PeriodBuilderImpl implements PeriodBuilder {\r
307 \r
308   protected BasicPeriodBuilderFactory.Settings settings;\r
309  \r
310   public Period create(long duration) {\r
311     return createWithReferenceDate(duration, System.currentTimeMillis());\r
312   }\r
313 \r
314   public long approximateDurationOf(TimeUnit unit) {\r
315     return BasicPeriodBuilderFactory.approximateDurationOf(unit);\r
316   }\r
317 \r
318   public Period createWithReferenceDate(long duration, long referenceDate) {\r
319     boolean inPast = duration < 0;\r
320     if (inPast) {\r
321       duration = -duration;\r
322     }\r
323     Period ts = settings.createLimited(duration, inPast);\r
324     if (ts == null) {\r
325       ts = handleCreate(duration, referenceDate, inPast);\r
326       if (ts == null) {\r
327         ts = Period.lessThan(1, settings.effectiveMinUnit()).inPast(inPast);\r
328       }\r
329     }\r
330     return ts;\r
331   }\r
332 \r
333   public PeriodBuilder withTimeZone(TimeZone timeZone) {\r
334       // ignore the time zone\r
335       return this;\r
336   }\r
337 \r
338   public PeriodBuilder withLocale(String localeName) {\r
339     BasicPeriodBuilderFactory.Settings newSettings = settings.setLocale(localeName);\r
340     if (newSettings != settings) {\r
341       return withSettings(newSettings);\r
342     }\r
343     return this;\r
344   }\r
345 \r
346   protected abstract PeriodBuilder withSettings(BasicPeriodBuilderFactory.Settings settingsToUse);\r
347 \r
348   protected abstract Period handleCreate(long duration, long referenceDate, \r
349                                          boolean inPast);\r
350 \r
351   protected PeriodBuilderImpl(BasicPeriodBuilderFactory.Settings settings) {\r
352     this.settings = settings;\r
353   }\r
354 }\r
355 \r
356 class FixedUnitBuilder extends PeriodBuilderImpl {\r
357   private TimeUnit unit;\r
358   \r
359   public static FixedUnitBuilder get(TimeUnit unit, BasicPeriodBuilderFactory.Settings settingsToUse) {\r
360     if (settingsToUse != null && (settingsToUse.effectiveSet() & (1 << unit.ordinal)) != 0) {\r
361       return new FixedUnitBuilder(unit, settingsToUse);\r
362     }\r
363     return null;\r
364   }\r
365 \r
366   FixedUnitBuilder(TimeUnit unit, BasicPeriodBuilderFactory.Settings settings) {\r
367     super(settings);\r
368     this.unit = unit;\r
369   }\r
370 \r
371   protected PeriodBuilder withSettings(BasicPeriodBuilderFactory.Settings settingsToUse) {\r
372     return get(unit, settingsToUse);\r
373   }\r
374 \r
375   protected Period handleCreate(long duration, long referenceDate, \r
376                                 boolean inPast) {\r
377     if (unit == null) {\r
378       return null;\r
379     }\r
380     long unitDuration = approximateDurationOf(unit);\r
381     return Period.at((float)((double)duration/unitDuration), unit)\r
382         .inPast(inPast);\r
383   }\r
384 }\r
385 \r
386 class SingleUnitBuilder extends PeriodBuilderImpl {\r
387   SingleUnitBuilder(BasicPeriodBuilderFactory.Settings settings) {\r
388     super(settings);\r
389   }\r
390 \r
391   public static SingleUnitBuilder get(BasicPeriodBuilderFactory.Settings settings) {\r
392     if (settings == null) {\r
393       return null;\r
394     }\r
395     return new SingleUnitBuilder(settings);\r
396   }\r
397 \r
398   protected PeriodBuilder withSettings(BasicPeriodBuilderFactory.Settings settingsToUse) {\r
399     return SingleUnitBuilder.get(settingsToUse);\r
400   }\r
401 \r
402   protected Period handleCreate(long duration, long referenceDate, \r
403                                 boolean inPast) {\r
404     short uset = settings.effectiveSet();\r
405     for (int i = 0; i < TimeUnit.units.length; ++i) {\r
406       if (0 != (uset & (1 << i))) {\r
407         TimeUnit unit = TimeUnit.units[i];\r
408         long unitDuration = approximateDurationOf(unit);\r
409         if (duration >= unitDuration) {\r
410           return Period.at((float)((double)duration/unitDuration), unit)\r
411               .inPast(inPast);\r
412         }\r
413       }\r
414     }\r
415     return null;\r
416   }\r
417 }\r
418 \r
419 class OneOrTwoUnitBuilder extends PeriodBuilderImpl {\r
420   OneOrTwoUnitBuilder(BasicPeriodBuilderFactory.Settings settings) {\r
421     super(settings);\r
422   }\r
423 \r
424   public static OneOrTwoUnitBuilder get(BasicPeriodBuilderFactory.Settings settings) {\r
425     if (settings == null) {\r
426       return null;\r
427     }\r
428     return new OneOrTwoUnitBuilder(settings);\r
429   }\r
430 \r
431   protected PeriodBuilder withSettings(BasicPeriodBuilderFactory.Settings settingsToUse) {\r
432     return OneOrTwoUnitBuilder.get(settingsToUse);\r
433   }\r
434 \r
435   protected Period handleCreate(long duration, long referenceDate, \r
436                                 boolean inPast) {\r
437     Period period = null;\r
438     short uset = settings.effectiveSet();\r
439     for (int i = 0; i < TimeUnit.units.length; ++i) {\r
440       if (0 != (uset & (1 << i))) {\r
441         TimeUnit unit = TimeUnit.units[i];\r
442         long unitDuration = approximateDurationOf(unit);\r
443         if (duration >= unitDuration || period != null) {\r
444           double count = (double)duration/unitDuration;\r
445           if (period == null) {\r
446             if (count >= 2) {\r
447               period = Period.at((float)count, unit);\r
448               break;\r
449             }\r
450             period = Period.at(1, unit).inPast(inPast);\r
451             duration -= unitDuration;\r
452           } else {\r
453             if (count >= 1) {\r
454               period.and((float)count, unit);\r
455             }\r
456             break;\r
457           }\r
458         }\r
459       }\r
460     }\r
461     return period;\r
462   }\r
463 }\r
464 \r
465 class MultiUnitBuilder extends PeriodBuilderImpl {\r
466   private int nPeriods;\r
467 \r
468   MultiUnitBuilder(int nPeriods, BasicPeriodBuilderFactory.Settings settings) {\r
469     super(settings);\r
470     this.nPeriods = nPeriods;\r
471   }\r
472 \r
473   public static MultiUnitBuilder get(int nPeriods, BasicPeriodBuilderFactory.Settings settings) {\r
474     if (nPeriods > 0 && settings != null) {\r
475       return new MultiUnitBuilder(nPeriods, settings);\r
476     }\r
477     return null;\r
478   }\r
479 \r
480   protected PeriodBuilder withSettings(BasicPeriodBuilderFactory.Settings settingsToUse) {\r
481     return MultiUnitBuilder.get(nPeriods, settingsToUse);\r
482   }\r
483 \r
484   protected Period handleCreate(long duration, long referenceDate, \r
485                                 boolean inPast) {\r
486     Period period = null;\r
487     int n = 0;\r
488     short uset = settings.effectiveSet();\r
489     for (int i = 0; i < TimeUnit.units.length; ++i) {\r
490       if (0 != (uset & (1 << i))) {\r
491         TimeUnit unit = TimeUnit.units[i];\r
492         if (n == nPeriods) {\r
493           break;\r
494         }\r
495         long unitDuration = approximateDurationOf(unit);\r
496         if (duration >= unitDuration || n > 0) {\r
497           ++n;\r
498           double count = (double)duration / unitDuration;\r
499           if (n < nPeriods) {\r
500             count = Math.floor(count);\r
501             duration -= (long)(count * unitDuration);\r
502           }\r
503           if (period == null) {\r
504             period = Period.at((float)count, unit).inPast(inPast);\r
505           } else {\r
506             period.and((float)count, unit);\r
507           }\r
508         }\r
509       }\r
510     }\r
511     return period;\r
512   }\r
513 }\r
514 \r