17935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/*
27935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert******************************************************************************
37935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert* Copyright (C) 2007-2010, 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 com.ibm.icu.impl.duration.BasicPeriodFormatterFactory.Customizations;
117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.duration.impl.DataRecord.ECountVariant;
127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.duration.impl.DataRecord.EMilliSupport;
137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.duration.impl.DataRecord.ESeparatorVariant;
147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.duration.impl.DataRecord.ETimeDirection;
157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.duration.impl.DataRecord.ETimeLimit;
167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.duration.impl.PeriodFormatterData;
177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/**
197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Core implementation class for PeriodFormatter.
207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */
217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertclass BasicPeriodFormatter implements PeriodFormatter {
227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  private BasicPeriodFormatterFactory factory;
237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  private String localeName;
247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  private PeriodFormatterData data;
257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  private Customizations customs;
267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  BasicPeriodFormatter(BasicPeriodFormatterFactory factory,
287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                       String localeName,
297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                       PeriodFormatterData data,
307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                       Customizations customs) {
317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    this.factory = factory;
327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    this.localeName = localeName;
337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    this.data = data;
347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    this.customs = customs;
357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  public String format(Period period) {
387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    if (!period.isSet()) {
397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      throw new IllegalArgumentException("period is not set");
407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    return format(period.timeLimit, period.inFuture, period.counts);
427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  public PeriodFormatter withLocale(String locName) {
457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    if (!this.localeName.equals(locName)) {
467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      PeriodFormatterData newData = factory.getData(locName);
477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      return new BasicPeriodFormatter(factory, locName, newData,
487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                      customs);
497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    return this;
517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  private String format(int tl, boolean inFuture, int[] counts) {
547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    int mask = 0;
557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    for (int i = 0; i < counts.length; ++i) {
567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      if (counts[i] > 0) {
577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        mask |= 1 << i;
587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      }
597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // if the data does not allow formatting of zero periods,
627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // remove these from consideration.  If the result has no
637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // periods set, return null to indicate we could not format
647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // the duration.
657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    if (!data.allowZero()) {
667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      for (int i = 0, m = 1; i < counts.length; ++i, m <<= 1) {
677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ((mask & m) != 0 && counts[i] == 1) {
687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert          mask &= ~m;
697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      }
717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      if (mask == 0) {
727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return null;
737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      }
747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // if the data does not allow milliseconds but milliseconds are
777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // set, merge them with seconds and force display of seconds to
787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // decimal with 3 places.
797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    boolean forceD3Seconds = false;
807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    if (data.useMilliseconds() != EMilliSupport.YES &&
817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        (mask & (1 << TimeUnit.MILLISECOND.ordinal)) != 0) {
827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      int sx = TimeUnit.SECOND.ordinal;
837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      int mx = TimeUnit.MILLISECOND.ordinal;
847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      int sf = 1 << sx;
857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      int mf = 1 << mx;
867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      switch (data.useMilliseconds()) {
877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        case EMilliSupport.WITH_SECONDS: {
887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert          // if there are seconds, merge with seconds, otherwise leave alone
897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert          if ((mask & sf) != 0) {
907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            counts[sx] += (counts[mx]-1)/1000;
917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            mask &= ~mf;
927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            forceD3Seconds = true;
937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert          }
947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } break;
957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        case EMilliSupport.NO: {
967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert          // merge with seconds, reset seconds before use just in case
977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert          if ((mask & sf) == 0) {
987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            mask |= sf;
997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            counts[sx] = 1;
1007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert          }
1017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert          counts[sx] += (counts[mx]-1)/1000;
1027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert          mask &= ~mf;
1037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert          forceD3Seconds = true;
1047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } break;
1057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      }
1067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // get the first and last units that are set.
1097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    int first = 0;
1107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    int last = counts.length - 1;
1117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    while (first < counts.length && (mask & (1 << first)) == 0) ++first;
1127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    while (last > first && (mask & (1 << last)) == 0) --last;
1137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // determine if there is any non-zero unit
1157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    boolean isZero = true;
1167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    for (int i = first; i <= last; ++i) {
1177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      if (((mask & (1 << i)) != 0) &&  counts[i] > 1) {
1187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        isZero = false;
1197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        break;
1207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      }
1217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    StringBuffer sb = new StringBuffer();
1247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // if we've been requested to not display a limit, or there are
1267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // no non-zero units, do not display the limit.
1277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    if (!customs.displayLimit || isZero) {
1287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      tl = ETimeLimit.NOLIMIT;
1297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // if we've been requested to not display the direction, or there
1327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // are no non-zero units, do not display the direction.
1337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    int td;
1347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    if (!customs.displayDirection || isZero) {
1357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      td = ETimeDirection.NODIRECTION;
1367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    } else {
1377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      td = inFuture ? ETimeDirection.FUTURE : ETimeDirection.PAST;
1387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // format the initial portion of the string before the units.
1417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // record whether we need to use a digit prefix (because the
1427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // initial portion forces it)
1437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    boolean useDigitPrefix = data.appendPrefix(tl, td, sb);
1447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // determine some formatting params and initial values
1467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    boolean multiple = first != last;
1477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    boolean wasSkipped = true; // no initial skip marker
1487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    boolean skipped = false;
1497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    boolean countSep = customs.separatorVariant != ESeparatorVariant.NONE;
1507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // loop for formatting the units
1527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    for (int i = first, j = i; i <= last; i = j) {
1537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      if (skipped) {
1547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // we didn't format the previous unit
1557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        data.appendSkippedUnit(sb);
1567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        skipped = false;
1577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        wasSkipped = true;
1587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      }
1597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      while (++j < last && (mask & (1 << j)) == 0) {
1617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        skipped = true; // skip
1627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      }
1637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      TimeUnit unit = TimeUnit.units[i];
1657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      int count = counts[i] - 1;
1667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      int cv = customs.countVariant;
1687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      if (i == last) {
1697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (forceD3Seconds) {
1707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert          cv = ECountVariant.DECIMAL3;
1717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // else leave unchanged
1737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      } else {
1747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        cv = ECountVariant.INTEGER;
1757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      }
1767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      boolean isLast = i == last;
1777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      boolean mustSkip = data.appendUnit(unit, count, cv, customs.unitVariant,
1787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                         countSep, useDigitPrefix, multiple, isLast, wasSkipped, sb);
1797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      skipped |= mustSkip;
1807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      wasSkipped = false;
1817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      if (customs.separatorVariant != ESeparatorVariant.NONE && j <= last) {
1837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean afterFirst = i == first;
1847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean beforeLast = j == last;
1857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean fullSep = customs.separatorVariant == ESeparatorVariant.FULL;
1867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        useDigitPrefix = data.appendUnitSeparator(unit, fullSep, afterFirst, beforeLast, sb);
1877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      } else {
1887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        useDigitPrefix = false;
1897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert      }
1907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    data.appendSuffix(tl, td, sb);
1927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    return sb.toString();
1947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  }
1957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert}
196