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