2 ******************************************************************************
\r
3 * Copyright (C) 2007-2008, 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.BasicPeriodFormatterFactory.Customizations;
\r
12 import com.ibm.icu.impl.duration.impl.DataRecord.*;
\r
13 import com.ibm.icu.impl.duration.impl.PeriodFormatterData;
\r
16 * Core implementation class for PeriodFormatter.
\r
18 class BasicPeriodFormatter implements PeriodFormatter {
\r
19 private BasicPeriodFormatterFactory factory;
\r
20 private String localeName;
\r
21 private PeriodFormatterData data;
\r
22 private Customizations customs;
\r
24 BasicPeriodFormatter(BasicPeriodFormatterFactory factory,
\r
26 PeriodFormatterData data,
\r
27 Customizations customs) {
\r
28 this.factory = factory;
\r
29 this.localeName = localeName;
\r
31 this.customs = customs;
\r
34 public String format(Period period) {
\r
35 if (!period.isSet()) {
\r
36 throw new IllegalArgumentException("period is not set");
\r
38 return format(period.timeLimit, period.inFuture, period.counts);
\r
41 public PeriodFormatter withLocale(String locName) {
\r
42 if (!this.localeName.equals(locName)) {
\r
43 PeriodFormatterData newData = factory.getData(locName);
\r
44 return new BasicPeriodFormatter(factory, locName, newData,
\r
50 private String format(int tl, boolean inFuture, int[] counts) {
\r
52 for (int i = 0; i < counts.length; ++i) {
\r
53 if (counts[i] > 0) {
\r
58 // if the data does not allow formatting of zero periods,
\r
59 // remove these from consideration. If the result has no
\r
60 // periods set, return null to indicate we could not format
\r
62 if (!data.allowZero()) {
\r
63 for (int i = 0, m = 1; i < counts.length; ++i, m <<= 1) {
\r
64 if ((mask & m) != 0 && counts[i] == 1) {
\r
73 // if the data does not allow milliseconds but milliseconds are
\r
74 // set, merge them with seconds and force display of seconds to
\r
75 // decimal with 3 places.
\r
76 boolean forceD3Seconds = false;
\r
77 if (data.useMilliseconds() != EMilliSupport.YES &&
\r
78 (mask & (1 << TimeUnit.MILLISECOND.ordinal)) != 0) {
\r
79 int sx = TimeUnit.SECOND.ordinal;
\r
80 int mx = TimeUnit.MILLISECOND.ordinal;
\r
83 switch (data.useMilliseconds()) {
\r
84 case EMilliSupport.WITH_SECONDS: {
\r
85 // if there are seconds, merge with seconds, otherwise leave alone
\r
86 if ((mask & sf) != 0) {
\r
87 counts[sx] += (counts[mx]-1)/1000;
\r
89 forceD3Seconds = true;
\r
92 case EMilliSupport.NO: {
\r
93 // merge with seconds, reset seconds before use just in case
\r
94 if ((mask & sf) == 0) {
\r
98 counts[sx] += (counts[mx]-1)/1000;
\r
100 forceD3Seconds = true;
\r
105 // get the first and last units that are set.
\r
107 int last = counts.length - 1;
\r
108 while (first < counts.length && (mask & (1 << first)) == 0) ++first;
\r
109 while (last > first && (mask & (1 << last)) == 0) --last;
\r
111 // determine if there is any non-zero unit
\r
112 boolean isZero = true;
\r
113 for (int i = first; i <= last; ++i) {
\r
114 if (((mask & (1 << i)) != 0) && counts[i] > 1) {
\r
120 StringBuffer sb = new StringBuffer();
\r
122 // if we've been requested to not display a limit, or there are
\r
123 // no non-zero units, do not display the limit.
\r
124 if (!customs.displayLimit || isZero) {
\r
125 tl = ETimeLimit.NOLIMIT;
\r
128 // if we've been requested to not display the direction, or there
\r
129 // are no non-zero units, do not display the direction.
\r
131 if (!customs.displayDirection || isZero) {
\r
132 td = ETimeDirection.NODIRECTION;
\r
134 td = inFuture ? ETimeDirection.FUTURE : ETimeDirection.PAST;
\r
137 // format the initial portion of the string before the units.
\r
138 // record whether we need to use a digit prefix (because the
\r
139 // initial portion forces it)
\r
140 boolean useDigitPrefix = data.appendPrefix(tl, td, sb);
\r
142 // determine some formatting params and initial values
\r
143 boolean multiple = first != last;
\r
144 boolean wasSkipped = true; // no initial skip marker
\r
145 boolean skipped = false;
\r
146 boolean countSep = customs.separatorVariant != ESeparatorVariant.NONE;
\r
148 // loop for formatting the units
\r
149 for (int i = first, j = i; i <= last; i = j) {
\r
151 // we didn't format the previous unit
\r
152 data.appendSkippedUnit(sb);
\r
157 while (++j < last && (mask & (1 << j)) == 0) {
\r
158 skipped = true; // skip
\r
161 TimeUnit unit = TimeUnit.units[i];
\r
162 int count = counts[i] - 1;
\r
164 int cv = customs.countVariant;
\r
166 if (forceD3Seconds) {
\r
167 cv = ECountVariant.DECIMAL3;
\r
169 // else leave unchanged
\r
171 cv = ECountVariant.INTEGER;
\r
173 boolean isLast = i == last;
\r
174 boolean mustSkip = data.appendUnit(unit, count, cv, customs.unitVariant,
\r
175 countSep, useDigitPrefix, multiple, isLast, wasSkipped, sb);
\r
176 skipped |= mustSkip;
\r
177 wasSkipped = false;
\r
179 if (customs.separatorVariant != ESeparatorVariant.NONE && j <= last) {
\r
180 boolean afterFirst = i == first;
\r
181 boolean beforeLast = j == last;
\r
182 boolean fullSep = customs.separatorVariant == ESeparatorVariant.FULL;
\r
183 useDigitPrefix = data.appendUnitSeparator(unit, fullSep, afterFirst, beforeLast, sb);
\r
185 useDigitPrefix = false;
\r
188 data.appendSuffix(tl, td, sb);
\r
190 return sb.toString();
\r