]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_4_2-src/main/classes/core/src/com/ibm/icu/impl/duration/BasicPeriodFormatter.java
go
[Dictionary.git] / jars / icu4j-4_4_2-src / main / classes / core / src / com / ibm / icu / impl / duration / BasicPeriodFormatter.java
1 /*\r
2 ******************************************************************************\r
3 * Copyright (C) 2007-2008, International Business Machines Corporation and   *\r
4 * others. All Rights Reserved.                                               *\r
5 ******************************************************************************\r
6 */\r
7 \r
8 package com.ibm.icu.impl.duration;\r
9 \r
10 import com.ibm.icu.impl.duration.BasicPeriodFormatterFactory.Customizations;\r
11 \r
12 import com.ibm.icu.impl.duration.impl.DataRecord.*;\r
13 import com.ibm.icu.impl.duration.impl.PeriodFormatterData;\r
14 \r
15 /**\r
16  * Core implementation class for PeriodFormatter.\r
17  */\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
23 \r
24   BasicPeriodFormatter(BasicPeriodFormatterFactory factory, \r
25                        String localeName,\r
26                        PeriodFormatterData data, \r
27                        Customizations customs) {\r
28     this.factory = factory;\r
29     this.localeName = localeName;\r
30     this.data = data;\r
31     this.customs = customs;\r
32   }\r
33 \r
34   public String format(Period period) {\r
35     if (!period.isSet()) {\r
36       throw new IllegalArgumentException("period is not set");\r
37     }\r
38     return format(period.timeLimit, period.inFuture, period.counts);\r
39   }\r
40 \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
45                                       customs);\r
46     }\r
47     return this;\r
48   }\r
49 \r
50   private String format(int tl, boolean inFuture, int[] counts) {\r
51     int mask = 0;\r
52     for (int i = 0; i < counts.length; ++i) {\r
53       if (counts[i] > 0) {\r
54         mask |= 1 << i;\r
55       }\r
56     }\r
57 \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
61     // the duration.\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
65           mask &= ~m;\r
66         }\r
67       }\r
68       if (mask == 0) {\r
69         return null;\r
70       }\r
71     }\r
72 \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
81       int sf = 1 << sx;\r
82       int mf = 1 << mx;\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
88             mask &= ~mf;\r
89             forceD3Seconds = true;\r
90           }\r
91         } break;\r
92         case EMilliSupport.NO: {\r
93           // merge with seconds, reset seconds before use just in case\r
94           if ((mask & sf) == 0) {\r
95             mask |= sf;\r
96             counts[sx] = 1;\r
97           }\r
98           counts[sx] += (counts[mx]-1)/1000;\r
99           mask &= ~mf;\r
100           forceD3Seconds = true;\r
101         } break;\r
102       }\r
103     }\r
104 \r
105     // get the first and last units that are set.\r
106     int first = 0;\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
110 \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
115         isZero = false;\r
116         break;\r
117       }\r
118     }\r
119 \r
120     StringBuffer sb = new StringBuffer();\r
121 \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
126     }\r
127 \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
130     int td;\r
131     if (!customs.displayDirection || isZero) {\r
132       td = ETimeDirection.NODIRECTION;\r
133     } else {\r
134       td = inFuture ? ETimeDirection.FUTURE : ETimeDirection.PAST;\r
135     }\r
136 \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
141 \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
147 \r
148     // loop for formatting the units\r
149     for (int i = first, j = i; i <= last; i = j) {\r
150       if (skipped) {\r
151         // we didn't format the previous unit\r
152         data.appendSkippedUnit(sb);\r
153         skipped = false;\r
154         wasSkipped = true;\r
155       }\r
156 \r
157       while (++j < last && (mask & (1 << j)) == 0) {\r
158         skipped = true; // skip\r
159       }\r
160 \r
161       TimeUnit unit = TimeUnit.units[i];\r
162       int count = counts[i] - 1;\r
163 \r
164       int cv = customs.countVariant;\r
165       if (i == last) {\r
166         if (forceD3Seconds) {\r
167           cv = ECountVariant.DECIMAL3;\r
168         }\r
169         // else leave unchanged\r
170       } else {\r
171         cv = ECountVariant.INTEGER;\r
172       }\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
178 \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
184       } else {\r
185         useDigitPrefix = false;\r
186       }\r
187     }\r
188     data.appendSuffix(tl, td, sb);\r
189 \r
190     return sb.toString();\r
191   }\r
192 }\r