]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_8_1_1/main/classes/core/src/com/ibm/icu/impl/duration/BasicPeriodFormatter.java
Added flags.
[Dictionary.git] / jars / icu4j-4_8_1_1 / main / classes / core / src / com / ibm / icu / impl / duration / BasicPeriodFormatter.java
1 /*
2 ******************************************************************************
3 * Copyright (C) 2007-2010, International Business Machines Corporation and   *
4 * others. All Rights Reserved.                                               *
5 ******************************************************************************
6 */
7
8 package com.ibm.icu.impl.duration;
9
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;
17
18 /**
19  * Core implementation class for PeriodFormatter.
20  */
21 class BasicPeriodFormatter implements PeriodFormatter {
22   private BasicPeriodFormatterFactory factory;
23   private String localeName;
24   private PeriodFormatterData data;
25   private Customizations customs;
26
27   BasicPeriodFormatter(BasicPeriodFormatterFactory factory, 
28                        String localeName,
29                        PeriodFormatterData data, 
30                        Customizations customs) {
31     this.factory = factory;
32     this.localeName = localeName;
33     this.data = data;
34     this.customs = customs;
35   }
36
37   public String format(Period period) {
38     if (!period.isSet()) {
39       throw new IllegalArgumentException("period is not set");
40     }
41     return format(period.timeLimit, period.inFuture, period.counts);
42   }
43
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, 
48                                       customs);
49     }
50     return this;
51   }
52
53   private String format(int tl, boolean inFuture, int[] counts) {
54     int mask = 0;
55     for (int i = 0; i < counts.length; ++i) {
56       if (counts[i] > 0) {
57         mask |= 1 << i;
58       }
59     }
60
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
64     // the duration.
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) {
68           mask &= ~m;
69         }
70       }
71       if (mask == 0) {
72         return null;
73       }
74     }
75
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;
84       int sf = 1 << sx;
85       int mf = 1 << mx;
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;
91             mask &= ~mf;
92             forceD3Seconds = true;
93           }
94         } break;
95         case EMilliSupport.NO: {
96           // merge with seconds, reset seconds before use just in case
97           if ((mask & sf) == 0) {
98             mask |= sf;
99             counts[sx] = 1;
100           }
101           counts[sx] += (counts[mx]-1)/1000;
102           mask &= ~mf;
103           forceD3Seconds = true;
104         } break;
105       }
106     }
107
108     // get the first and last units that are set.
109     int first = 0;
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;
113
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) {
118         isZero = false;
119         break;
120       }
121     }
122
123     StringBuffer sb = new StringBuffer();
124
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;
129     }
130
131     // if we've been requested to not display the direction, or there
132     // are no non-zero units, do not display the direction.
133     int td;
134     if (!customs.displayDirection || isZero) {
135       td = ETimeDirection.NODIRECTION;
136     } else {
137       td = inFuture ? ETimeDirection.FUTURE : ETimeDirection.PAST;
138     }
139
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);
144
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;
150
151     // loop for formatting the units
152     for (int i = first, j = i; i <= last; i = j) {
153       if (skipped) {
154         // we didn't format the previous unit
155         data.appendSkippedUnit(sb);
156         skipped = false;
157         wasSkipped = true;
158       }
159
160       while (++j < last && (mask & (1 << j)) == 0) {
161         skipped = true; // skip
162       }
163
164       TimeUnit unit = TimeUnit.units[i];
165       int count = counts[i] - 1;
166
167       int cv = customs.countVariant;
168       if (i == last) {
169         if (forceD3Seconds) {
170           cv = ECountVariant.DECIMAL3;
171         }
172         // else leave unchanged
173       } else {
174         cv = ECountVariant.INTEGER;
175       }
176       boolean isLast = i == last;
177       boolean mustSkip = data.appendUnit(unit, count, cv, customs.unitVariant, 
178                                          countSep, useDigitPrefix, multiple, isLast, wasSkipped, sb);
179       skipped |= mustSkip;
180       wasSkipped = false;
181
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);
187       } else {
188         useDigitPrefix = false;
189       }
190     }
191     data.appendSuffix(tl, td, sb);
192
193     return sb.toString();
194   }
195 }