2 ******************************************************************************
3 * Copyright (C) 2007-2010, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 ******************************************************************************
8 package com.ibm.icu.impl.duration;
10 import com.ibm.icu.impl.duration.BasicPeriodFormatterFactory.Customizations;
11 import com.ibm.icu.impl.duration.impl.DataRecord.ECountVariant;
12 import com.ibm.icu.impl.duration.impl.DataRecord.EMilliSupport;
13 import com.ibm.icu.impl.duration.impl.DataRecord.ESeparatorVariant;
14 import com.ibm.icu.impl.duration.impl.DataRecord.ETimeDirection;
15 import com.ibm.icu.impl.duration.impl.DataRecord.ETimeLimit;
16 import com.ibm.icu.impl.duration.impl.PeriodFormatterData;
19 * Core implementation class for PeriodFormatter.
21 class BasicPeriodFormatter implements PeriodFormatter {
22 private BasicPeriodFormatterFactory factory;
23 private String localeName;
24 private PeriodFormatterData data;
25 private Customizations customs;
27 BasicPeriodFormatter(BasicPeriodFormatterFactory factory,
29 PeriodFormatterData data,
30 Customizations customs) {
31 this.factory = factory;
32 this.localeName = localeName;
34 this.customs = customs;
37 public String format(Period period) {
38 if (!period.isSet()) {
39 throw new IllegalArgumentException("period is not set");
41 return format(period.timeLimit, period.inFuture, period.counts);
44 public PeriodFormatter withLocale(String locName) {
45 if (!this.localeName.equals(locName)) {
46 PeriodFormatterData newData = factory.getData(locName);
47 return new BasicPeriodFormatter(factory, locName, newData,
53 private String format(int tl, boolean inFuture, int[] counts) {
55 for (int i = 0; i < counts.length; ++i) {
61 // if the data does not allow formatting of zero periods,
62 // remove these from consideration. If the result has no
63 // periods set, return null to indicate we could not format
65 if (!data.allowZero()) {
66 for (int i = 0, m = 1; i < counts.length; ++i, m <<= 1) {
67 if ((mask & m) != 0 && counts[i] == 1) {
76 // if the data does not allow milliseconds but milliseconds are
77 // set, merge them with seconds and force display of seconds to
78 // decimal with 3 places.
79 boolean forceD3Seconds = false;
80 if (data.useMilliseconds() != EMilliSupport.YES &&
81 (mask & (1 << TimeUnit.MILLISECOND.ordinal)) != 0) {
82 int sx = TimeUnit.SECOND.ordinal;
83 int mx = TimeUnit.MILLISECOND.ordinal;
86 switch (data.useMilliseconds()) {
87 case EMilliSupport.WITH_SECONDS: {
88 // if there are seconds, merge with seconds, otherwise leave alone
89 if ((mask & sf) != 0) {
90 counts[sx] += (counts[mx]-1)/1000;
92 forceD3Seconds = true;
95 case EMilliSupport.NO: {
96 // merge with seconds, reset seconds before use just in case
97 if ((mask & sf) == 0) {
101 counts[sx] += (counts[mx]-1)/1000;
103 forceD3Seconds = true;
108 // get the first and last units that are set.
110 int last = counts.length - 1;
111 while (first < counts.length && (mask & (1 << first)) == 0) ++first;
112 while (last > first && (mask & (1 << last)) == 0) --last;
114 // determine if there is any non-zero unit
115 boolean isZero = true;
116 for (int i = first; i <= last; ++i) {
117 if (((mask & (1 << i)) != 0) && counts[i] > 1) {
123 StringBuffer sb = new StringBuffer();
125 // if we've been requested to not display a limit, or there are
126 // no non-zero units, do not display the limit.
127 if (!customs.displayLimit || isZero) {
128 tl = ETimeLimit.NOLIMIT;
131 // if we've been requested to not display the direction, or there
132 // are no non-zero units, do not display the direction.
134 if (!customs.displayDirection || isZero) {
135 td = ETimeDirection.NODIRECTION;
137 td = inFuture ? ETimeDirection.FUTURE : ETimeDirection.PAST;
140 // format the initial portion of the string before the units.
141 // record whether we need to use a digit prefix (because the
142 // initial portion forces it)
143 boolean useDigitPrefix = data.appendPrefix(tl, td, sb);
145 // determine some formatting params and initial values
146 boolean multiple = first != last;
147 boolean wasSkipped = true; // no initial skip marker
148 boolean skipped = false;
149 boolean countSep = customs.separatorVariant != ESeparatorVariant.NONE;
151 // loop for formatting the units
152 for (int i = first, j = i; i <= last; i = j) {
154 // we didn't format the previous unit
155 data.appendSkippedUnit(sb);
160 while (++j < last && (mask & (1 << j)) == 0) {
161 skipped = true; // skip
164 TimeUnit unit = TimeUnit.units[i];
165 int count = counts[i] - 1;
167 int cv = customs.countVariant;
169 if (forceD3Seconds) {
170 cv = ECountVariant.DECIMAL3;
172 // else leave unchanged
174 cv = ECountVariant.INTEGER;
176 boolean isLast = i == last;
177 boolean mustSkip = data.appendUnit(unit, count, cv, customs.unitVariant,
178 countSep, useDigitPrefix, multiple, isLast, wasSkipped, sb);
182 if (customs.separatorVariant != ESeparatorVariant.NONE && j <= last) {
183 boolean afterFirst = i == first;
184 boolean beforeLast = j == last;
185 boolean fullSep = customs.separatorVariant == ESeparatorVariant.FULL;
186 useDigitPrefix = data.appendUnitSeparator(unit, fullSep, afterFirst, beforeLast, sb);
188 useDigitPrefix = false;
191 data.appendSuffix(tl, td, sb);
193 return sb.toString();