17935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/*
27935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert******************************************************************************
37935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert* Copyright (C) 2007-2011, International Business Machines Corporation and   *
47935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert* others. All Rights Reserved.                                               *
57935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert******************************************************************************
67935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert*/
77935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
87935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpackage com.ibm.icu.impl.duration;
97935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.TimeZone;
117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.duration.impl.DataRecord;
137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.duration.impl.PeriodFormatterData;
147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.duration.impl.PeriodFormatterDataService;
157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/**
177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Default implementation of PeriodBuilderFactory.  This creates builders that
187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * use approximate durations.
197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */
207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertclass BasicPeriodBuilderFactory implements PeriodBuilderFactory {
217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  private PeriodFormatterDataService ds;
227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  private Settings settings;
237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  private static final short allBits = 0xff;
257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  BasicPeriodBuilderFactory(PeriodFormatterDataService ds) {
277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    this.ds = ds;
287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    this.settings = new Settings();
297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  static long approximateDurationOf(TimeUnit unit) {
327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    return TimeUnit.approxDurations[unit.ordinal];
337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  class Settings {
367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    boolean inUse;
377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    short uset = allBits;
387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    TimeUnit maxUnit = TimeUnit.YEAR;
397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    TimeUnit minUnit = TimeUnit.MILLISECOND;
407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    int maxLimit;
417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    int minLimit;
427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    boolean allowZero = true;
437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    boolean weeksAloneOnly;
447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    boolean allowMillis = true;
457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    Settings setUnits(int uset) {
477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      if (this.uset == uset) {
487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return this;
497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      }
507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      Settings result = inUse ? copy() : this;
517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      result.uset = (short)uset;
537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      if ((uset & allBits) == allBits) {
557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        result.uset = allBits;
567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        result.maxUnit = TimeUnit.YEAR;
577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        result.minUnit = TimeUnit.MILLISECOND;
587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      } else {
597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int lastUnit = -1;
607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = 0; i < TimeUnit.units.length; ++i) {
617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert          if (0 != (uset & (1 << i))) {
627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (lastUnit == -1) {
637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              result.maxUnit = TimeUnit.units[i];
647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            lastUnit = i;
667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert          }
677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (lastUnit == -1) {
697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // currently empty, but this might be transient so no fail
707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            result.minUnit = result.maxUnit = null;
717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            result.minUnit = TimeUnit.units[lastUnit];
737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      }
757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      return result;
777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    short effectiveSet() {
807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      if (allowMillis) {
817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return uset;
827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      }
837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      return (short)(uset & ~(1 << TimeUnit.MILLISECOND.ordinal));
847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    TimeUnit effectiveMinUnit() {
877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (allowMillis || minUnit != TimeUnit.MILLISECOND) {
887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return minUnit;
897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // -1 to skip millisecond
917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = TimeUnit.units.length - 1; --i >= 0;) {
927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (0 != (uset & (1 << i))) {
937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return TimeUnit.units[i];
947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return TimeUnit.SECOND; // default for pathological case
977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    Settings setMaxLimit(float maxLimit) {
1007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      int val = maxLimit <= 0 ? 0 : (int)(maxLimit*1000);
1017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      if (maxLimit == val) {
1027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return this;
1037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      }
1047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      Settings result = inUse ? copy() : this;
1057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      result.maxLimit = val;
1067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      return result;
1077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    Settings setMinLimit(float minLimit) {
1107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      int val = minLimit <= 0 ? 0 : (int)(minLimit*1000);
1117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      if (minLimit == val) {
1127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return this;
1137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      }
1147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      Settings result = inUse ? copy() : this;
1157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      result.minLimit = val;
1167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      return result;
1177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    Settings setAllowZero(boolean allow) {
1207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      if (this.allowZero == allow) {
1217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return this;
1227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      }
1237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      Settings result = inUse ? copy() : this;
1247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      result.allowZero = allow;
1257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      return result;
1267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    Settings setWeeksAloneOnly(boolean weeksAlone) {
1297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      if (this.weeksAloneOnly == weeksAlone) {
1307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return this;
1317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      }
1327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      Settings result = inUse ? copy() : this;
1337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      result.weeksAloneOnly = weeksAlone;
1347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      return result;
1357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    Settings setAllowMilliseconds(boolean allowMillis) {
1387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      if (this.allowMillis == allowMillis) {
1397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return this;
1407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      }
1417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      Settings result = inUse ? copy() : this;
1427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      result.allowMillis = allowMillis;
1437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      return result;
1447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    Settings setLocale(String localeName) {
1477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      PeriodFormatterData data = ds.get(localeName);
1487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      return this
1497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        .setAllowZero(data.allowZero())
1507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        .setWeeksAloneOnly(data.weeksAloneOnly())
1517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        .setAllowMilliseconds(data.useMilliseconds() != DataRecord.EMilliSupport.NO);
1527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    Settings setInUse() {
1557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      inUse = true;
1567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      return this;
1577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    Period createLimited(long duration, boolean inPast) {
1607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      if (maxLimit > 0) {
1617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert          long maxUnitDuration = approximateDurationOf(maxUnit);
1627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert          if (duration * 1000 > maxLimit * maxUnitDuration) {
1637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              return Period.moreThan(maxLimit/1000f, maxUnit).inPast(inPast);
1647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert          }
1657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      }
1667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      if (minLimit > 0) {
1687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert          TimeUnit emu = effectiveMinUnit();
1697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert          long emud = approximateDurationOf(emu);
1707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert          long eml = (emu == minUnit) ? minLimit :
1717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              Math.max(1000, (approximateDurationOf(minUnit) * minLimit) / emud);
1727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert          if (duration * 1000 < eml * emud) {
1737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              return Period.lessThan(eml/1000f, emu).inPast(inPast);
1747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert          }
1757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      }
1767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      return null;
1777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Settings copy() {
1807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Settings result = new Settings();
1817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        result.inUse = inUse;
1827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        result.uset = uset;
1837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        result.maxUnit = maxUnit;
1847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        result.minUnit = minUnit;
1857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        result.maxLimit = maxLimit;
1867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        result.minLimit = minLimit;
1877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        result.allowZero = allowZero;
1887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        result.weeksAloneOnly = weeksAloneOnly;
1897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        result.allowMillis = allowMillis;
1907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return result;
1917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
1937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  public PeriodBuilderFactory setAvailableUnitRange(TimeUnit minUnit,
1957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                                    TimeUnit maxUnit) {
1967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    int uset = 0;
1977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    for (int i = maxUnit.ordinal; i <= minUnit.ordinal; ++i) {
1987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        uset |= 1 << i;
1997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    if (uset == 0) {
2017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        throw new IllegalArgumentException("range " + minUnit + " to " + maxUnit + " is empty");
2027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    settings = settings.setUnits(uset);
2047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    return this;
2057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
2067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  public PeriodBuilderFactory setUnitIsAvailable(TimeUnit unit,
2087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                                      boolean available) {
2097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    int uset = settings.uset;
2107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    if (available) {
2117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      uset |= 1 << unit.ordinal;
2127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    } else {
2137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      uset &= ~(1 << unit.ordinal);
2147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    settings = settings.setUnits(uset);
2167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    return this;
2177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
2187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  public PeriodBuilderFactory setMaxLimit(float maxLimit) {
2207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    settings = settings.setMaxLimit(maxLimit);
2217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    return this;
2227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
2237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  public PeriodBuilderFactory setMinLimit(float minLimit) {
2257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    settings = settings.setMinLimit(minLimit);
2267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    return this;
2277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
2287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  public PeriodBuilderFactory setAllowZero(boolean allow) {
2307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    settings = settings.setAllowZero(allow);
2317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    return this;
2327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
2337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  public PeriodBuilderFactory setWeeksAloneOnly(boolean aloneOnly) {
2357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    settings = settings.setWeeksAloneOnly(aloneOnly);
2367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    return this;
2377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
2387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  public PeriodBuilderFactory setAllowMilliseconds(boolean allow) {
2407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    settings = settings.setAllowMilliseconds(allow);
2417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    return this;
2427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
2437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  public PeriodBuilderFactory setLocale(String localeName) {
2457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    settings = settings.setLocale(localeName);
2467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    return this;
2477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
2487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  public PeriodBuilderFactory setTimeZone(TimeZone timeZone) {
2507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      // ignore this
2517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      return this;
2527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
2537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  private Settings getSettings() {
2557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    if (settings.effectiveSet() == 0) {
2567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      return null;
2577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    return settings.setInUse();
2597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
2607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  /**
2627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert   * Return a builder that represents relative time in terms of the single
2637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert   * given TimeUnit
2647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert   *
2657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert   * @param unit the single TimeUnit with which to represent times
2667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert   * @return a builder
2677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert   */
2687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  public PeriodBuilder getFixedUnitBuilder(TimeUnit unit) {
2697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    return FixedUnitBuilder.get(unit, getSettings());
2707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
2717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  /**
2737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert   * Return a builder that represents relative time in terms of the
2747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert   * largest period less than or equal to the duration.
2757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert   *
2767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert   * @return a builder
2777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert   */
2787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  public PeriodBuilder getSingleUnitBuilder() {
2797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    return SingleUnitBuilder.get(getSettings());
2807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
2817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  /**
2837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert   * Return a builder that formats the largest one or two periods,
2847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert   * Starting with the largest period less than or equal to the duration.
2857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert   * It formats two periods if the first period has a count &lt; 2
2867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert   * and the next period has a count &gt;= 1.
2877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert   *
2887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert   * @return a builder
2897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert   */
2907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  public PeriodBuilder getOneOrTwoUnitBuilder() {
2917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    return OneOrTwoUnitBuilder.get(getSettings());
2927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
2937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  /**
2957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert   * Return a builder that formats the given number of periods,
2967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert   * starting with the largest period less than or equal to the
2977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert   * duration.
2987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert   *
2997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert   * @return a builder
3007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert   */
3017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  public PeriodBuilder getMultiUnitBuilder(int periodCount) {
3027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    return MultiUnitBuilder.get(periodCount, getSettings());
3037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
3047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert}
3057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertabstract class PeriodBuilderImpl implements PeriodBuilder {
3077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  protected BasicPeriodBuilderFactory.Settings settings;
3097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  public Period create(long duration) {
3117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    return createWithReferenceDate(duration, System.currentTimeMillis());
3127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
3137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  public long approximateDurationOf(TimeUnit unit) {
3157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    return BasicPeriodBuilderFactory.approximateDurationOf(unit);
3167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
3177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  public Period createWithReferenceDate(long duration, long referenceDate) {
3197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    boolean inPast = duration < 0;
3207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    if (inPast) {
3217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      duration = -duration;
3227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    Period ts = settings.createLimited(duration, inPast);
3247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    if (ts == null) {
3257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      ts = handleCreate(duration, referenceDate, inPast);
3267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      if (ts == null) {
3277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        ts = Period.lessThan(1, settings.effectiveMinUnit()).inPast(inPast);
3287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      }
3297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    return ts;
3317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
3327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  public PeriodBuilder withTimeZone(TimeZone timeZone) {
3347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      // ignore the time zone
3357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      return this;
3367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
3377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  public PeriodBuilder withLocale(String localeName) {
3397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    BasicPeriodBuilderFactory.Settings newSettings = settings.setLocale(localeName);
3407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    if (newSettings != settings) {
3417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      return withSettings(newSettings);
3427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    return this;
3447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
3457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  protected abstract PeriodBuilder withSettings(BasicPeriodBuilderFactory.Settings settingsToUse);
3477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  protected abstract Period handleCreate(long duration, long referenceDate,
3497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                         boolean inPast);
3507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  protected PeriodBuilderImpl(BasicPeriodBuilderFactory.Settings settings) {
3527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    this.settings = settings;
3537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
3547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert}
3557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertclass FixedUnitBuilder extends PeriodBuilderImpl {
3577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  private TimeUnit unit;
3587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  public static FixedUnitBuilder get(TimeUnit unit, BasicPeriodBuilderFactory.Settings settingsToUse) {
3607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    if (settingsToUse != null && (settingsToUse.effectiveSet() & (1 << unit.ordinal)) != 0) {
3617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      return new FixedUnitBuilder(unit, settingsToUse);
3627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    return null;
3647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
3657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  FixedUnitBuilder(TimeUnit unit, BasicPeriodBuilderFactory.Settings settings) {
3677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    super(settings);
3687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    this.unit = unit;
3697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
3707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  protected PeriodBuilder withSettings(BasicPeriodBuilderFactory.Settings settingsToUse) {
3727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    return get(unit, settingsToUse);
3737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
3747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  protected Period handleCreate(long duration, long referenceDate,
3767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                boolean inPast) {
3777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    if (unit == null) {
3787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      return null;
3797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    long unitDuration = approximateDurationOf(unit);
3817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    return Period.at((float)((double)duration/unitDuration), unit)
3827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        .inPast(inPast);
3837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
3847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert}
3857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertclass SingleUnitBuilder extends PeriodBuilderImpl {
3877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  SingleUnitBuilder(BasicPeriodBuilderFactory.Settings settings) {
3887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    super(settings);
3897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
3907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  public static SingleUnitBuilder get(BasicPeriodBuilderFactory.Settings settings) {
3927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    if (settings == null) {
3937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      return null;
3947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    return new SingleUnitBuilder(settings);
3967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
3977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  protected PeriodBuilder withSettings(BasicPeriodBuilderFactory.Settings settingsToUse) {
3997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    return SingleUnitBuilder.get(settingsToUse);
4007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
4017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  protected Period handleCreate(long duration, long referenceDate,
4037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                boolean inPast) {
4047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    short uset = settings.effectiveSet();
4057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    for (int i = 0; i < TimeUnit.units.length; ++i) {
4067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      if (0 != (uset & (1 << i))) {
4077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        TimeUnit unit = TimeUnit.units[i];
4087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        long unitDuration = approximateDurationOf(unit);
4097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (duration >= unitDuration) {
4107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert          return Period.at((float)((double)duration/unitDuration), unit)
4117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              .inPast(inPast);
4127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      }
4147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    return null;
4167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
4177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert}
4187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertclass OneOrTwoUnitBuilder extends PeriodBuilderImpl {
4207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  OneOrTwoUnitBuilder(BasicPeriodBuilderFactory.Settings settings) {
4217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    super(settings);
4227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
4237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  public static OneOrTwoUnitBuilder get(BasicPeriodBuilderFactory.Settings settings) {
4257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    if (settings == null) {
4267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      return null;
4277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    return new OneOrTwoUnitBuilder(settings);
4297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
4307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  protected PeriodBuilder withSettings(BasicPeriodBuilderFactory.Settings settingsToUse) {
4327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    return OneOrTwoUnitBuilder.get(settingsToUse);
4337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
4347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  protected Period handleCreate(long duration, long referenceDate,
4367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                boolean inPast) {
4377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    Period period = null;
4387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    short uset = settings.effectiveSet();
4397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    for (int i = 0; i < TimeUnit.units.length; ++i) {
4407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      if (0 != (uset & (1 << i))) {
4417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        TimeUnit unit = TimeUnit.units[i];
4427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        long unitDuration = approximateDurationOf(unit);
4437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (duration >= unitDuration || period != null) {
4447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert          double count = (double)duration/unitDuration;
4457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert          if (period == null) {
4467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (count >= 2) {
4477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              period = Period.at((float)count, unit);
4487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              break;
4497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
4507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            period = Period.at(1, unit).inPast(inPast);
4517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            duration -= unitDuration;
4527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert          } else {
4537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (count >= 1) {
4547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert              period = period.and((float)count, unit);
4557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
4567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            break;
4577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert          }
4587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      }
4607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    return period;
4627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
4637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert}
4647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertclass MultiUnitBuilder extends PeriodBuilderImpl {
4667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  private int nPeriods;
4677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  MultiUnitBuilder(int nPeriods, BasicPeriodBuilderFactory.Settings settings) {
4697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    super(settings);
4707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    this.nPeriods = nPeriods;
4717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
4727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  public static MultiUnitBuilder get(int nPeriods, BasicPeriodBuilderFactory.Settings settings) {
4747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    if (nPeriods > 0 && settings != null) {
4757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      return new MultiUnitBuilder(nPeriods, settings);
4767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    return null;
4787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
4797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  protected PeriodBuilder withSettings(BasicPeriodBuilderFactory.Settings settingsToUse) {
4817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    return MultiUnitBuilder.get(nPeriods, settingsToUse);
4827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
4837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  protected Period handleCreate(long duration, long referenceDate,
4857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                boolean inPast) {
4867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    Period period = null;
4877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    int n = 0;
4887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    short uset = settings.effectiveSet();
4897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    for (int i = 0; i < TimeUnit.units.length; ++i) {
4907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      if (0 != (uset & (1 << i))) {
4917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        TimeUnit unit = TimeUnit.units[i];
4927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (n == nPeriods) {
4937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert          break;
4947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        long unitDuration = approximateDurationOf(unit);
4967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (duration >= unitDuration || n > 0) {
4977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert          ++n;
4987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert          double count = (double)duration / unitDuration;
4997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert          if (n < nPeriods) {
5007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            count = Math.floor(count);
5017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            duration -= (long)(count * unitDuration);
5027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert          }
5037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert          if (period == null) {
5047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            period = Period.at((float)count, unit).inPast(inPast);
5057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert          } else {
5067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            period = period.and((float)count, unit);
5077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert          }
5087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      }
5107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
5117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    return period;
5127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
5137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert}
5147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
515