20 |
ilm |
1 |
/*
|
|
|
2 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
|
|
|
3 |
*
|
|
|
4 |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
|
|
|
5 |
*
|
|
|
6 |
* The contents of this file are subject to the terms of the GNU General Public License Version 3
|
|
|
7 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
|
|
|
8 |
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
|
|
|
9 |
* language governing permissions and limitations under the License.
|
|
|
10 |
*
|
|
|
11 |
* When distributing the software, include this License Header Notice in each file.
|
|
|
12 |
*/
|
|
|
13 |
|
|
|
14 |
package org.openconcerto.openoffice.style.data;
|
|
|
15 |
|
|
|
16 |
import org.openconcerto.openoffice.ODPackage;
|
|
|
17 |
import org.openconcerto.openoffice.StyleProperties;
|
|
|
18 |
import org.openconcerto.openoffice.XMLVersion;
|
|
|
19 |
import org.openconcerto.openoffice.spreadsheet.CellStyle;
|
|
|
20 |
|
|
|
21 |
import java.math.BigDecimal;
|
|
|
22 |
import java.text.DecimalFormat;
|
|
|
23 |
import java.text.DecimalFormatSymbols;
|
|
|
24 |
import java.text.SimpleDateFormat;
|
|
|
25 |
import java.util.Calendar;
|
|
|
26 |
import java.util.Date;
|
|
|
27 |
import java.util.List;
|
|
|
28 |
import java.util.Locale;
|
|
|
29 |
|
|
|
30 |
import org.jdom.Attribute;
|
|
|
31 |
import org.jdom.Element;
|
|
|
32 |
import org.jdom.Namespace;
|
|
|
33 |
|
|
|
34 |
// from section 16.27.10 in v1.2-cs01-part1
|
|
|
35 |
public class DateStyle extends DataStyle {
|
|
|
36 |
|
|
|
37 |
// see http://download.oracle.com/javase/6/docs/technotes/guides/intl/calendar.doc.html
|
|
|
38 |
private static final Locale BUDDHIST_LOCALE = new Locale("th", "TH");
|
|
|
39 |
private static final Locale JAPANESE_LOCALE = new Locale("ja", "JP", "JP");
|
|
|
40 |
private static final Locale GREGORIAN_LOCALE = new Locale("fr", "FR");
|
|
|
41 |
|
|
|
42 |
public static final DataStyleDesc<DateStyle> DESC = new DataStyleDesc<DateStyle>(DateStyle.class, XMLVersion.OD, "date-style", "N") {
|
|
|
43 |
@Override
|
|
|
44 |
public DateStyle create(ODPackage pkg, Element e) {
|
|
|
45 |
return new DateStyle(pkg, e);
|
|
|
46 |
}
|
|
|
47 |
};
|
|
|
48 |
|
21 |
ilm |
49 |
static final boolean isShort(final Element elem) {
|
|
|
50 |
// in OOo the default is short
|
|
|
51 |
return !"long".equals(elem.getAttributeValue("style", elem.getNamespace("number")));
|
20 |
ilm |
52 |
}
|
|
|
53 |
|
|
|
54 |
public static final Locale getLocale(final Element elem) {
|
|
|
55 |
final Locale res;
|
|
|
56 |
final String country = elem.getAttributeValue("country", elem.getNamespace());
|
|
|
57 |
final String lang = elem.getAttributeValue("language", elem.getNamespace());
|
|
|
58 |
if (lang != null) {
|
|
|
59 |
res = new Locale(lang, country == null ? "" : country);
|
|
|
60 |
} else {
|
|
|
61 |
res = Locale.getDefault();
|
|
|
62 |
}
|
|
|
63 |
return res;
|
|
|
64 |
}
|
|
|
65 |
|
|
|
66 |
private static final Locale getCalendarLocale(final Element elem, Locale defaultLocale) {
|
|
|
67 |
final Locale res;
|
|
|
68 |
final String cal = elem.getAttributeValue("calendar", elem.getNamespace());
|
|
|
69 |
if (cal == null) {
|
|
|
70 |
res = defaultLocale;
|
|
|
71 |
} else if ("buddhist".equals(cal)) {
|
|
|
72 |
res = BUDDHIST_LOCALE;
|
|
|
73 |
} else if ("gengou".equals(cal)) {
|
|
|
74 |
res = JAPANESE_LOCALE;
|
|
|
75 |
} else if ("gregorian".equals(cal)) {
|
|
|
76 |
res = GREGORIAN_LOCALE;
|
|
|
77 |
} else {
|
|
|
78 |
throw new IllegalArgumentException("Unsupported calendar : " + cal);
|
|
|
79 |
}
|
|
|
80 |
return res;
|
|
|
81 |
}
|
|
|
82 |
|
|
|
83 |
private static final Locale getCalendarLocale(final Locale locale) {
|
|
|
84 |
final Locale res;
|
|
|
85 |
if (locale.equals(BUDDHIST_LOCALE) || locale.equals(JAPANESE_LOCALE)) {
|
|
|
86 |
res = locale;
|
|
|
87 |
} else {
|
|
|
88 |
res = GREGORIAN_LOCALE;
|
|
|
89 |
}
|
|
|
90 |
return res;
|
|
|
91 |
}
|
|
|
92 |
|
21 |
ilm |
93 |
static String formatSecondFraction(final Locale styleLocale, final BigDecimal seconds, final int decPlaces) {
|
|
|
94 |
if (decPlaces > 0) {
|
|
|
95 |
final DecimalFormat decFormat = new DecimalFormat();
|
|
|
96 |
decFormat.setDecimalFormatSymbols(new DecimalFormatSymbols(styleLocale));
|
|
|
97 |
decFormat.setMinimumIntegerDigits(0);
|
|
|
98 |
decFormat.setMaximumIntegerDigits(0);
|
|
|
99 |
decFormat.setMinimumFractionDigits(decPlaces);
|
|
|
100 |
decFormat.setMaximumFractionDigits(decPlaces);
|
|
|
101 |
// .12 or .578
|
|
|
102 |
return decFormat.format(seconds);
|
|
|
103 |
} else {
|
|
|
104 |
return "";
|
|
|
105 |
}
|
|
|
106 |
}
|
|
|
107 |
|
20 |
ilm |
108 |
public DateStyle(final ODPackage pkg, Element elem) {
|
|
|
109 |
super(pkg, elem, Date.class);
|
|
|
110 |
}
|
|
|
111 |
|
|
|
112 |
@Override
|
|
|
113 |
public String format(Object o, CellStyle defaultStyle, boolean lenient) {
|
|
|
114 |
final Date d = o instanceof Calendar ? ((Calendar) o).getTime() : (Date) o;
|
|
|
115 |
final Namespace numberNS = this.getElement().getNamespace();
|
|
|
116 |
final Locale styleLocale = getLocale(getElement());
|
|
|
117 |
final Locale styleCalendarLocale = getCalendarLocale(styleLocale);
|
|
|
118 |
final StringBuilder res = new StringBuilder();
|
|
|
119 |
|
|
|
120 |
Locale currentCalendarLocale = styleCalendarLocale;
|
|
|
121 |
final StringBuilder sb = new StringBuilder();
|
|
|
122 |
|
|
|
123 |
@SuppressWarnings("unchecked")
|
|
|
124 |
final List<Element> children = this.getElement().getChildren();
|
|
|
125 |
for (final Element elem : children) {
|
|
|
126 |
if (elem.getNamespace().equals(numberNS)) {
|
|
|
127 |
final Locale calendarLocaleElem = getCalendarLocale(elem, styleCalendarLocale);
|
|
|
128 |
if (!calendarLocaleElem.equals(currentCalendarLocale)) {
|
|
|
129 |
if (sb.length() > 0) {
|
|
|
130 |
res.append(new SimpleDateFormat(sb.toString(), currentCalendarLocale).format(d));
|
|
|
131 |
sb.setLength(0);
|
|
|
132 |
}
|
|
|
133 |
currentCalendarLocale = calendarLocaleElem;
|
|
|
134 |
}
|
|
|
135 |
|
|
|
136 |
if (elem.getName().equals("text")) {
|
|
|
137 |
DataStyle.addStringLiteral(sb, elem.getText());
|
|
|
138 |
} else if (elem.getName().equals("era")) {
|
|
|
139 |
sb.append(isShort(elem) ? "G" : "GGGG");
|
|
|
140 |
} else if (elem.getName().equals("year")) {
|
|
|
141 |
sb.append(isShort(elem) ? "yy" : "yyyy");
|
|
|
142 |
} else if (elem.getName().equals("quarter")) {
|
|
|
143 |
final int quarter = Calendar.getInstance(GREGORIAN_LOCALE).get(Calendar.MONTH) / 3 + 1;
|
|
|
144 |
// TODO localize and honor short/long style
|
|
|
145 |
reportError("Quarters are not localized", lenient);
|
|
|
146 |
DataStyle.addStringLiteral(sb, isShort(elem) ? "Q" + quarter : "Q" + quarter);
|
|
|
147 |
} else if (elem.getName().equals("month")) {
|
|
|
148 |
final Attribute possessive = elem.getAttribute("possessive-form", numberNS);
|
|
|
149 |
if (possessive != null)
|
|
|
150 |
reportError("Ignoring " + possessive, lenient);
|
|
|
151 |
if (!StyleProperties.parseBoolean(elem.getAttributeValue("textual", numberNS), false))
|
|
|
152 |
sb.append(isShort(elem) ? "M" : "MM");
|
|
|
153 |
else
|
|
|
154 |
sb.append(isShort(elem) ? "MMM" : "MMMM");
|
|
|
155 |
} else if (elem.getName().equals("week-of-year")) {
|
|
|
156 |
sb.append("w");
|
|
|
157 |
} else if (elem.getName().equals("day")) {
|
|
|
158 |
sb.append(isShort(elem) ? "d" : "dd");
|
|
|
159 |
} else if (elem.getName().equals("day-of-week")) {
|
|
|
160 |
sb.append(isShort(elem) ? "E" : "EEEE");
|
|
|
161 |
} else if (elem.getName().equals("am-pm")) {
|
|
|
162 |
sb.append("a");
|
|
|
163 |
} else if (elem.getName().equals("hours")) {
|
|
|
164 |
// see 16.27.22 : If a <number:am-pm> element is contained in a date or time
|
|
|
165 |
// style, hours are displayed using values from 1 to 12 only.
|
|
|
166 |
if (getElement().getChild("am-pm", numberNS) == null)
|
|
|
167 |
sb.append(isShort(elem) ? "H" : "HH");
|
|
|
168 |
else
|
|
|
169 |
sb.append(isShort(elem) ? "h" : "hh");
|
|
|
170 |
} else if (elem.getName().equals("minutes")) {
|
|
|
171 |
sb.append(isShort(elem) ? "m" : "mm");
|
|
|
172 |
} else if (elem.getName().equals("seconds")) {
|
|
|
173 |
sb.append(isShort(elem) ? "s" : "ss");
|
|
|
174 |
final int decPlaces = StyleProperties.parseInt(elem.getAttributeValue("decimal-places", numberNS), 0);
|
|
|
175 |
if (decPlaces > 0) {
|
|
|
176 |
// use styleLocale since <seconds> hasn't @calendar
|
|
|
177 |
final Calendar cal = Calendar.getInstance(styleLocale);
|
|
|
178 |
cal.setTime(d);
|
|
|
179 |
final BigDecimal secondFractions = new BigDecimal(cal.get(Calendar.MILLISECOND)).movePointLeft(3);
|
|
|
180 |
assert secondFractions.compareTo(BigDecimal.ONE) < 0;
|
21 |
ilm |
181 |
final String fractionPart = formatSecondFraction(styleLocale, secondFractions, decPlaces);
|
20 |
ilm |
182 |
DataStyle.addStringLiteral(sb, fractionPart);
|
|
|
183 |
}
|
|
|
184 |
}
|
|
|
185 |
}
|
|
|
186 |
}
|
|
|
187 |
return new SimpleDateFormat(sb.toString(), currentCalendarLocale).format(d);
|
|
|
188 |
}
|
|
|
189 |
}
|