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