2 ******************************************************************************
\r
3 * Copyright (C) 2007-2009, International Business Machines Corporation and *
\r
4 * others. All Rights Reserved. *
\r
5 ******************************************************************************
\r
8 package com.ibm.icu.impl.duration;
\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
14 import java.util.TimeZone;
\r
17 * Default implementation of PeriodBuilderFactory. This creates builders that
\r
18 * use approximate durations.
\r
20 class BasicPeriodBuilderFactory implements PeriodBuilderFactory {
\r
21 private PeriodFormatterDataService ds;
\r
22 private Settings settings;
\r
24 private static final short allBits = 0xff;
\r
26 BasicPeriodBuilderFactory(PeriodFormatterDataService ds) {
\r
28 this.settings = new Settings();
\r
31 static long approximateDurationOf(TimeUnit unit) {
\r
32 return TimeUnit.approxDurations[unit.ordinal];
\r
37 short uset = allBits;
\r
38 TimeUnit maxUnit = TimeUnit.YEAR;
\r
39 TimeUnit minUnit = TimeUnit.MILLISECOND;
\r
42 boolean allowZero = true;
\r
43 boolean weeksAloneOnly;
\r
44 boolean allowMillis = true;
\r
46 Settings setUnits(int uset) {
\r
47 if (this.uset == uset) {
\r
50 Settings result = inUse ? copy() : this;
\r
52 result.uset = (short)uset;
\r
54 if ((uset & allBits) == allBits) {
\r
55 result.uset = allBits;
\r
56 result.maxUnit = TimeUnit.YEAR;
\r
57 result.minUnit = TimeUnit.MILLISECOND;
\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
68 if (lastUnit == -1) {
\r
69 // currently empty, but this might be transient so no fail
\r
70 result.minUnit = result.maxUnit = null;
\r
72 result.minUnit = TimeUnit.units[lastUnit];
\r
79 short effectiveSet() {
\r
83 return (short)(uset & ~(1 << TimeUnit.MILLISECOND.ordinal));
\r
86 TimeUnit effectiveMinUnit() {
\r
87 if (allowMillis || minUnit != TimeUnit.MILLISECOND) {
\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
96 return TimeUnit.SECOND; // default for pathological case
\r
99 Settings setMaxLimit(float maxLimit) {
\r
100 int val = maxLimit <= 0 ? 0 : (int)(maxLimit*1000);
\r
101 if (maxLimit == val) {
\r
104 Settings result = inUse ? copy() : this;
\r
105 result.maxLimit = val;
\r
109 Settings setMinLimit(float minLimit) {
\r
110 int val = minLimit <= 0 ? 0 : (int)(minLimit*1000);
\r
111 if (minLimit == val) {
\r
114 Settings result = inUse ? copy() : this;
\r
115 result.minLimit = val;
\r
119 Settings setAllowZero(boolean allow) {
\r
120 if (this.allowZero == allow) {
\r
123 Settings result = inUse ? copy() : this;
\r
124 result.allowZero = allow;
\r
128 Settings setWeeksAloneOnly(boolean weeksAlone) {
\r
129 if (this.weeksAloneOnly == weeksAlone) {
\r
132 Settings result = inUse ? copy() : this;
\r
133 result.weeksAloneOnly = weeksAlone;
\r
137 Settings setAllowMilliseconds(boolean allowMillis) {
\r
138 if (this.allowMillis == allowMillis) {
\r
141 Settings result = inUse ? copy() : this;
\r
142 result.allowMillis = allowMillis;
\r
146 Settings setLocale(String localeName) {
\r
147 PeriodFormatterData data = ds.get(localeName);
\r
149 .setAllowZero(data.allowZero())
\r
150 .setWeeksAloneOnly(data.weeksAloneOnly())
\r
151 .setAllowMilliseconds(data.useMilliseconds() != DataRecord.EMilliSupport.NO);
\r
154 Settings setInUse() {
\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
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
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
194 public PeriodBuilderFactory setAvailableUnitRange(TimeUnit minUnit,
\r
195 TimeUnit maxUnit) {
\r
197 for (int i = maxUnit.ordinal; i <= minUnit.ordinal; ++i) {
\r
201 throw new IllegalArgumentException("range " + minUnit + " to " + maxUnit + " is empty");
\r
203 settings = settings.setUnits(uset);
\r
207 public PeriodBuilderFactory setUnitIsAvailable(TimeUnit unit,
\r
208 boolean available) {
\r
209 int uset = settings.uset;
\r
211 uset |= 1 << unit.ordinal;
\r
213 uset &= ~(1 << unit.ordinal);
\r
215 settings = settings.setUnits(uset);
\r
219 public PeriodBuilderFactory setMaxLimit(float maxLimit) {
\r
220 settings = settings.setMaxLimit(maxLimit);
\r
224 public PeriodBuilderFactory setMinLimit(float minLimit) {
\r
225 settings = settings.setMinLimit(minLimit);
\r
229 public PeriodBuilderFactory setAllowZero(boolean allow) {
\r
230 settings = settings.setAllowZero(allow);
\r
234 public PeriodBuilderFactory setWeeksAloneOnly(boolean aloneOnly) {
\r
235 settings = settings.setWeeksAloneOnly(aloneOnly);
\r
239 public PeriodBuilderFactory setAllowMilliseconds(boolean allow) {
\r
240 settings = settings.setAllowMilliseconds(allow);
\r
244 public PeriodBuilderFactory setLocale(String localeName) {
\r
245 settings = settings.setLocale(localeName);
\r
249 public PeriodBuilderFactory setTimeZone(TimeZone timeZone) {
\r
254 private Settings getSettings() {
\r
255 if (settings.effectiveSet() == 0) {
\r
258 return settings.setInUse();
\r
262 * Return a builder that represents relative time in terms of the single
\r
265 * @param unit the single TimeUnit with which to represent times
\r
266 * @return a builder
\r
268 public PeriodBuilder getFixedUnitBuilder(TimeUnit unit) {
\r
269 return FixedUnitBuilder.get(unit, getSettings());
\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
276 * @return a builder
\r
278 public PeriodBuilder getSingleUnitBuilder() {
\r
279 return SingleUnitBuilder.get(getSettings());
\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 < 2
\r
286 * and the next period has a count >= 1.
\r
288 * @return a builder
\r
290 public PeriodBuilder getOneOrTwoUnitBuilder() {
\r
291 return OneOrTwoUnitBuilder.get(getSettings());
\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
299 * @return a builder
\r
301 public PeriodBuilder getMultiUnitBuilder(int periodCount) {
\r
302 return MultiUnitBuilder.get(periodCount, getSettings());
\r
306 abstract class PeriodBuilderImpl implements PeriodBuilder {
\r
308 protected BasicPeriodBuilderFactory.Settings settings;
\r
310 public Period create(long duration) {
\r
311 return createWithReferenceDate(duration, System.currentTimeMillis());
\r
314 public long approximateDurationOf(TimeUnit unit) {
\r
315 return BasicPeriodBuilderFactory.approximateDurationOf(unit);
\r
318 public Period createWithReferenceDate(long duration, long referenceDate) {
\r
319 boolean inPast = duration < 0;
\r
321 duration = -duration;
\r
323 Period ts = settings.createLimited(duration, inPast);
\r
325 ts = handleCreate(duration, referenceDate, inPast);
\r
327 ts = Period.lessThan(1, settings.effectiveMinUnit()).inPast(inPast);
\r
333 public PeriodBuilder withTimeZone(TimeZone timeZone) {
\r
334 // ignore the time zone
\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
346 protected abstract PeriodBuilder withSettings(BasicPeriodBuilderFactory.Settings settingsToUse);
\r
348 protected abstract Period handleCreate(long duration, long referenceDate,
\r
351 protected PeriodBuilderImpl(BasicPeriodBuilderFactory.Settings settings) {
\r
352 this.settings = settings;
\r
356 class FixedUnitBuilder extends PeriodBuilderImpl {
\r
357 private TimeUnit unit;
\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
366 FixedUnitBuilder(TimeUnit unit, BasicPeriodBuilderFactory.Settings settings) {
\r
371 protected PeriodBuilder withSettings(BasicPeriodBuilderFactory.Settings settingsToUse) {
\r
372 return get(unit, settingsToUse);
\r
375 protected Period handleCreate(long duration, long referenceDate,
\r
377 if (unit == null) {
\r
380 long unitDuration = approximateDurationOf(unit);
\r
381 return Period.at((float)((double)duration/unitDuration), unit)
\r
386 class SingleUnitBuilder extends PeriodBuilderImpl {
\r
387 SingleUnitBuilder(BasicPeriodBuilderFactory.Settings settings) {
\r
391 public static SingleUnitBuilder get(BasicPeriodBuilderFactory.Settings settings) {
\r
392 if (settings == null) {
\r
395 return new SingleUnitBuilder(settings);
\r
398 protected PeriodBuilder withSettings(BasicPeriodBuilderFactory.Settings settingsToUse) {
\r
399 return SingleUnitBuilder.get(settingsToUse);
\r
402 protected Period handleCreate(long duration, long referenceDate,
\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
419 class OneOrTwoUnitBuilder extends PeriodBuilderImpl {
\r
420 OneOrTwoUnitBuilder(BasicPeriodBuilderFactory.Settings settings) {
\r
424 public static OneOrTwoUnitBuilder get(BasicPeriodBuilderFactory.Settings settings) {
\r
425 if (settings == null) {
\r
428 return new OneOrTwoUnitBuilder(settings);
\r
431 protected PeriodBuilder withSettings(BasicPeriodBuilderFactory.Settings settingsToUse) {
\r
432 return OneOrTwoUnitBuilder.get(settingsToUse);
\r
435 protected Period handleCreate(long duration, long referenceDate,
\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
447 period = Period.at((float)count, unit);
\r
450 period = Period.at(1, unit).inPast(inPast);
\r
451 duration -= unitDuration;
\r
454 period.and((float)count, unit);
\r
465 class MultiUnitBuilder extends PeriodBuilderImpl {
\r
466 private int nPeriods;
\r
468 MultiUnitBuilder(int nPeriods, BasicPeriodBuilderFactory.Settings settings) {
\r
470 this.nPeriods = nPeriods;
\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
480 protected PeriodBuilder withSettings(BasicPeriodBuilderFactory.Settings settingsToUse) {
\r
481 return MultiUnitBuilder.get(nPeriods, settingsToUse);
\r
484 protected Period handleCreate(long duration, long referenceDate,
\r
486 Period period = null;
\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
495 long unitDuration = approximateDurationOf(unit);
\r
496 if (duration >= unitDuration || n > 0) {
\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
503 if (period == null) {
\r
504 period = Period.at((float)count, unit).inPast(inPast);
\r
506 period.and((float)count, unit);
\r