com.ibm.icu.impl.duration.BasicPeriodBuilderFactory Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of icu4j Show documentation
Show all versions of icu4j Show documentation
International Component for Unicode for Java (ICU4J) is a mature, widely used Java library
providing Unicode and Globalization support
/*
******************************************************************************
* Copyright (C) 2007-2011, International Business Machines Corporation and *
* others. All Rights Reserved. *
******************************************************************************
*/
package com.ibm.icu.impl.duration;
import java.util.TimeZone;
import com.ibm.icu.impl.duration.impl.DataRecord;
import com.ibm.icu.impl.duration.impl.PeriodFormatterData;
import com.ibm.icu.impl.duration.impl.PeriodFormatterDataService;
/**
* Default implementation of PeriodBuilderFactory. This creates builders that
* use approximate durations.
*/
class BasicPeriodBuilderFactory implements PeriodBuilderFactory {
private PeriodFormatterDataService ds;
private Settings settings;
private static final short allBits = 0xff;
BasicPeriodBuilderFactory(PeriodFormatterDataService ds) {
this.ds = ds;
this.settings = new Settings();
}
static long approximateDurationOf(TimeUnit unit) {
return TimeUnit.approxDurations[unit.ordinal];
}
class Settings {
boolean inUse;
short uset = allBits;
TimeUnit maxUnit = TimeUnit.YEAR;
TimeUnit minUnit = TimeUnit.MILLISECOND;
int maxLimit;
int minLimit;
boolean allowZero = true;
boolean weeksAloneOnly;
boolean allowMillis = true;
Settings setUnits(int uset) {
if (this.uset == uset) {
return this;
}
Settings result = inUse ? copy() : this;
result.uset = (short)uset;
if ((uset & allBits) == allBits) {
result.uset = allBits;
result.maxUnit = TimeUnit.YEAR;
result.minUnit = TimeUnit.MILLISECOND;
} else {
int lastUnit = -1;
for (int i = 0; i < TimeUnit.units.length; ++i) {
if (0 != (uset & (1 << i))) {
if (lastUnit == -1) {
result.maxUnit = TimeUnit.units[i];
}
lastUnit = i;
}
}
if (lastUnit == -1) {
// currently empty, but this might be transient so no fail
result.minUnit = result.maxUnit = null;
} else {
result.minUnit = TimeUnit.units[lastUnit];
}
}
return result;
}
short effectiveSet() {
if (allowMillis) {
return uset;
}
return (short)(uset & ~(1 << TimeUnit.MILLISECOND.ordinal));
}
TimeUnit effectiveMinUnit() {
if (allowMillis || minUnit != TimeUnit.MILLISECOND) {
return minUnit;
}
// -1 to skip millisecond
for (int i = TimeUnit.units.length - 1; --i >= 0;) {
if (0 != (uset & (1 << i))) {
return TimeUnit.units[i];
}
}
return TimeUnit.SECOND; // default for pathological case
}
Settings setMaxLimit(float maxLimit) {
int val = maxLimit <= 0 ? 0 : (int)(maxLimit*1000);
if (maxLimit == val) {
return this;
}
Settings result = inUse ? copy() : this;
result.maxLimit = val;
return result;
}
Settings setMinLimit(float minLimit) {
int val = minLimit <= 0 ? 0 : (int)(minLimit*1000);
if (minLimit == val) {
return this;
}
Settings result = inUse ? copy() : this;
result.minLimit = val;
return result;
}
Settings setAllowZero(boolean allow) {
if (this.allowZero == allow) {
return this;
}
Settings result = inUse ? copy() : this;
result.allowZero = allow;
return result;
}
Settings setWeeksAloneOnly(boolean weeksAlone) {
if (this.weeksAloneOnly == weeksAlone) {
return this;
}
Settings result = inUse ? copy() : this;
result.weeksAloneOnly = weeksAlone;
return result;
}
Settings setAllowMilliseconds(boolean allowMillis) {
if (this.allowMillis == allowMillis) {
return this;
}
Settings result = inUse ? copy() : this;
result.allowMillis = allowMillis;
return result;
}
Settings setLocale(String localeName) {
PeriodFormatterData data = ds.get(localeName);
return this
.setAllowZero(data.allowZero())
.setWeeksAloneOnly(data.weeksAloneOnly())
.setAllowMilliseconds(data.useMilliseconds() != DataRecord.EMilliSupport.NO);
}
Settings setInUse() {
inUse = true;
return this;
}
Period createLimited(long duration, boolean inPast) {
if (maxLimit > 0) {
long maxUnitDuration = approximateDurationOf(maxUnit);
if (duration * 1000 > maxLimit * maxUnitDuration) {
return Period.moreThan(maxLimit/1000f, maxUnit).inPast(inPast);
}
}
if (minLimit > 0) {
TimeUnit emu = effectiveMinUnit();
long emud = approximateDurationOf(emu);
long eml = (emu == minUnit) ? minLimit :
Math.max(1000, (approximateDurationOf(minUnit) * minLimit) / emud);
if (duration * 1000 < eml * emud) {
return Period.lessThan(eml/1000f, emu).inPast(inPast);
}
}
return null;
}
public Settings copy() {
Settings result = new Settings();
result.inUse = inUse;
result.uset = uset;
result.maxUnit = maxUnit;
result.minUnit = minUnit;
result.maxLimit = maxLimit;
result.minLimit = minLimit;
result.allowZero = allowZero;
result.weeksAloneOnly = weeksAloneOnly;
result.allowMillis = allowMillis;
return result;
}
}
public PeriodBuilderFactory setAvailableUnitRange(TimeUnit minUnit,
TimeUnit maxUnit) {
int uset = 0;
for (int i = maxUnit.ordinal; i <= minUnit.ordinal; ++i) {
uset |= 1 << i;
}
if (uset == 0) {
throw new IllegalArgumentException("range " + minUnit + " to " + maxUnit + " is empty");
}
settings = settings.setUnits(uset);
return this;
}
public PeriodBuilderFactory setUnitIsAvailable(TimeUnit unit,
boolean available) {
int uset = settings.uset;
if (available) {
uset |= 1 << unit.ordinal;
} else {
uset &= ~(1 << unit.ordinal);
}
settings = settings.setUnits(uset);
return this;
}
public PeriodBuilderFactory setMaxLimit(float maxLimit) {
settings = settings.setMaxLimit(maxLimit);
return this;
}
public PeriodBuilderFactory setMinLimit(float minLimit) {
settings = settings.setMinLimit(minLimit);
return this;
}
public PeriodBuilderFactory setAllowZero(boolean allow) {
settings = settings.setAllowZero(allow);
return this;
}
public PeriodBuilderFactory setWeeksAloneOnly(boolean aloneOnly) {
settings = settings.setWeeksAloneOnly(aloneOnly);
return this;
}
public PeriodBuilderFactory setAllowMilliseconds(boolean allow) {
settings = settings.setAllowMilliseconds(allow);
return this;
}
public PeriodBuilderFactory setLocale(String localeName) {
settings = settings.setLocale(localeName);
return this;
}
public PeriodBuilderFactory setTimeZone(TimeZone timeZone) {
// ignore this
return this;
}
private Settings getSettings() {
if (settings.effectiveSet() == 0) {
return null;
}
return settings.setInUse();
}
/**
* Return a builder that represents relative time in terms of the single
* given TimeUnit
*
* @param unit the single TimeUnit with which to represent times
* @return a builder
*/
public PeriodBuilder getFixedUnitBuilder(TimeUnit unit) {
return FixedUnitBuilder.get(unit, getSettings());
}
/**
* Return a builder that represents relative time in terms of the
* largest period less than or equal to the duration.
*
* @return a builder
*/
public PeriodBuilder getSingleUnitBuilder() {
return SingleUnitBuilder.get(getSettings());
}
/**
* Return a builder that formats the largest one or two periods,
* Starting with the largest period less than or equal to the duration.
* It formats two periods if the first period has a count < 2
* and the next period has a count >= 1.
*
* @return a builder
*/
public PeriodBuilder getOneOrTwoUnitBuilder() {
return OneOrTwoUnitBuilder.get(getSettings());
}
/**
* Return a builder that formats the given number of periods,
* starting with the largest period less than or equal to the
* duration.
*
* @return a builder
*/
public PeriodBuilder getMultiUnitBuilder(int periodCount) {
return MultiUnitBuilder.get(periodCount, getSettings());
}
}
abstract class PeriodBuilderImpl implements PeriodBuilder {
protected BasicPeriodBuilderFactory.Settings settings;
public Period create(long duration) {
return createWithReferenceDate(duration, System.currentTimeMillis());
}
public long approximateDurationOf(TimeUnit unit) {
return BasicPeriodBuilderFactory.approximateDurationOf(unit);
}
public Period createWithReferenceDate(long duration, long referenceDate) {
boolean inPast = duration < 0;
if (inPast) {
duration = -duration;
}
Period ts = settings.createLimited(duration, inPast);
if (ts == null) {
ts = handleCreate(duration, referenceDate, inPast);
if (ts == null) {
ts = Period.lessThan(1, settings.effectiveMinUnit()).inPast(inPast);
}
}
return ts;
}
public PeriodBuilder withTimeZone(TimeZone timeZone) {
// ignore the time zone
return this;
}
public PeriodBuilder withLocale(String localeName) {
BasicPeriodBuilderFactory.Settings newSettings = settings.setLocale(localeName);
if (newSettings != settings) {
return withSettings(newSettings);
}
return this;
}
protected abstract PeriodBuilder withSettings(BasicPeriodBuilderFactory.Settings settingsToUse);
protected abstract Period handleCreate(long duration, long referenceDate,
boolean inPast);
protected PeriodBuilderImpl(BasicPeriodBuilderFactory.Settings settings) {
this.settings = settings;
}
}
class FixedUnitBuilder extends PeriodBuilderImpl {
private TimeUnit unit;
public static FixedUnitBuilder get(TimeUnit unit, BasicPeriodBuilderFactory.Settings settingsToUse) {
if (settingsToUse != null && (settingsToUse.effectiveSet() & (1 << unit.ordinal)) != 0) {
return new FixedUnitBuilder(unit, settingsToUse);
}
return null;
}
FixedUnitBuilder(TimeUnit unit, BasicPeriodBuilderFactory.Settings settings) {
super(settings);
this.unit = unit;
}
protected PeriodBuilder withSettings(BasicPeriodBuilderFactory.Settings settingsToUse) {
return get(unit, settingsToUse);
}
protected Period handleCreate(long duration, long referenceDate,
boolean inPast) {
if (unit == null) {
return null;
}
long unitDuration = approximateDurationOf(unit);
return Period.at((float)((double)duration/unitDuration), unit)
.inPast(inPast);
}
}
class SingleUnitBuilder extends PeriodBuilderImpl {
SingleUnitBuilder(BasicPeriodBuilderFactory.Settings settings) {
super(settings);
}
public static SingleUnitBuilder get(BasicPeriodBuilderFactory.Settings settings) {
if (settings == null) {
return null;
}
return new SingleUnitBuilder(settings);
}
protected PeriodBuilder withSettings(BasicPeriodBuilderFactory.Settings settingsToUse) {
return SingleUnitBuilder.get(settingsToUse);
}
protected Period handleCreate(long duration, long referenceDate,
boolean inPast) {
short uset = settings.effectiveSet();
for (int i = 0; i < TimeUnit.units.length; ++i) {
if (0 != (uset & (1 << i))) {
TimeUnit unit = TimeUnit.units[i];
long unitDuration = approximateDurationOf(unit);
if (duration >= unitDuration) {
return Period.at((float)((double)duration/unitDuration), unit)
.inPast(inPast);
}
}
}
return null;
}
}
class OneOrTwoUnitBuilder extends PeriodBuilderImpl {
OneOrTwoUnitBuilder(BasicPeriodBuilderFactory.Settings settings) {
super(settings);
}
public static OneOrTwoUnitBuilder get(BasicPeriodBuilderFactory.Settings settings) {
if (settings == null) {
return null;
}
return new OneOrTwoUnitBuilder(settings);
}
protected PeriodBuilder withSettings(BasicPeriodBuilderFactory.Settings settingsToUse) {
return OneOrTwoUnitBuilder.get(settingsToUse);
}
protected Period handleCreate(long duration, long referenceDate,
boolean inPast) {
Period period = null;
short uset = settings.effectiveSet();
for (int i = 0; i < TimeUnit.units.length; ++i) {
if (0 != (uset & (1 << i))) {
TimeUnit unit = TimeUnit.units[i];
long unitDuration = approximateDurationOf(unit);
if (duration >= unitDuration || period != null) {
double count = (double)duration/unitDuration;
if (period == null) {
if (count >= 2) {
period = Period.at((float)count, unit);
break;
}
period = Period.at(1, unit).inPast(inPast);
duration -= unitDuration;
} else {
if (count >= 1) {
period = period.and((float)count, unit);
}
break;
}
}
}
}
return period;
}
}
class MultiUnitBuilder extends PeriodBuilderImpl {
private int nPeriods;
MultiUnitBuilder(int nPeriods, BasicPeriodBuilderFactory.Settings settings) {
super(settings);
this.nPeriods = nPeriods;
}
public static MultiUnitBuilder get(int nPeriods, BasicPeriodBuilderFactory.Settings settings) {
if (nPeriods > 0 && settings != null) {
return new MultiUnitBuilder(nPeriods, settings);
}
return null;
}
protected PeriodBuilder withSettings(BasicPeriodBuilderFactory.Settings settingsToUse) {
return MultiUnitBuilder.get(nPeriods, settingsToUse);
}
protected Period handleCreate(long duration, long referenceDate,
boolean inPast) {
Period period = null;
int n = 0;
short uset = settings.effectiveSet();
for (int i = 0; i < TimeUnit.units.length; ++i) {
if (0 != (uset & (1 << i))) {
TimeUnit unit = TimeUnit.units[i];
if (n == nPeriods) {
break;
}
long unitDuration = approximateDurationOf(unit);
if (duration >= unitDuration || n > 0) {
++n;
double count = (double)duration / unitDuration;
if (n < nPeriods) {
count = Math.floor(count);
duration -= (long)(count * unitDuration);
}
if (period == null) {
period = Period.at((float)count, unit).inPast(inPast);
} else {
period = period.and((float)count, unit);
}
}
}
}
return period;
}
}