OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Rev 180 | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 180 Rev 185
Line 1... Line 1...
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-2019 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.
Line 16... Line 16...
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.CollectionUtils;
21
import org.openconcerto.utils.convertor.NumberConvertor;
22
import org.openconcerto.utils.convertor.NumberConvertor;
22
 
23
 
23
import java.math.BigDecimal;
24
import java.math.BigDecimal;
24
import java.text.DecimalFormat;
25
import java.text.DecimalFormat;
25
import java.text.DecimalFormatSymbols;
26
import java.text.DecimalFormatSymbols;
26
import java.text.SimpleDateFormat;
27
import java.text.SimpleDateFormat;
-
 
28
import java.util.ArrayList;
27
import java.util.Calendar;
29
import java.util.Calendar;
28
import java.util.Date;
30
import java.util.Date;
29
import java.util.GregorianCalendar;
31
import java.util.GregorianCalendar;
30
import java.util.List;
32
import java.util.List;
31
import java.util.Locale;
33
import java.util.Locale;
-
 
34
import java.util.SortedMap;
-
 
35
import java.util.TreeMap;
-
 
36
import java.util.function.Consumer;
32
 
37
 
33
import org.jdom.Attribute;
38
import org.jdom.Attribute;
34
import org.jdom.Element;
39
import org.jdom.Element;
35
import org.jdom.Namespace;
40
import org.jdom.Namespace;
36
 
41
 
-
 
42
import com.ibm.icu.text.DateTimePatternGenerator;
-
 
43
 
37
// from section 16.27.10 in v1.2-cs01-part1
44
// from section 16.27.10 in v1.2-cs01-part1
38
public class DateStyle extends DataStyle {
45
public class DateStyle extends DataStyle {
39
 
46
 
-
 
47
    private static final String AUTOMATIC_ORDER_ATTRNAME = "automatic-order";
-
 
48
 
-
 
49
    private static final String FORMAT_SOURCE_ATTRNAME = "format-source";
-
 
50
    private static final String FORMAT_SOURCE_FIXED = "fixed";
-
 
51
    private static final String FORMAT_SOURCE_LANG = "language";
-
 
52
 
40
    // see http://download.oracle.com/javase/6/docs/technotes/guides/intl/calendar.doc.html
53
    // 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"));
54
    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"));
55
    private static final Calendar JAPANESE_CAL = Calendar.getInstance(new Locale("ja", "JP", "JP"));
43
    private static final Calendar GREGORIAN_CAL = new GregorianCalendar();
56
    private static final Calendar GREGORIAN_CAL = new GregorianCalendar();
44
 
57
 
Line 52... Line 65...
52
    static final boolean isShort(final Element elem) {
65
    static final boolean isShort(final Element elem) {
53
        // in OOo the default is short
66
        // in OOo the default is short
54
        return !"long".equals(elem.getAttributeValue("style", elem.getNamespace("number")));
67
        return !"long".equals(elem.getAttributeValue("style", elem.getNamespace("number")));
55
    }
68
    }
56
 
69
 
-
 
70
    // with LO 6.4 only year,month,day,day-of-week have variable length
-
 
71
    static final boolean isShort(final Element elem, final boolean fixed, final Locale locale) {
-
 
72
        if (fixed)
-
 
73
            return isShort(elem);
-
 
74
        else
-
 
75
            return Locale.US.equals(locale);
-
 
76
    }
-
 
77
 
57
    private static final Calendar getCalendar(final Element elem, Calendar defaultCal) {
78
    private static final Calendar getCalendar(final Element elem, Calendar defaultCal) {
58
        final Calendar res;
79
        final Calendar res;
59
        final String cal = elem.getAttributeValue("calendar", elem.getNamespace());
80
        final String cal = elem.getAttributeValue("calendar", elem.getNamespace());
60
        if (cal == null) {
81
        if (cal == null) {
61
            res = defaultCal;
82
            res = defaultCal;
Line 96... Line 117...
96
            return getEpoch().getDate(NumberConvertor.toBigDecimal((Number) o));
117
            return getEpoch().getDate(NumberConvertor.toBigDecimal((Number) o));
97
        else
118
        else
98
            return null;
119
            return null;
99
    }
120
    }
100
 
121
 
-
 
122
    public final boolean isFormatSourceFixed() {
-
 
123
        return this.getElement().getAttributeValue(FORMAT_SOURCE_ATTRNAME, this.getElement().getNamespace(), FORMAT_SOURCE_FIXED).equals(FORMAT_SOURCE_FIXED);
-
 
124
    }
-
 
125
 
-
 
126
    final void setFormatSourceFixed(final boolean b) {
-
 
127
        if (b) {
-
 
128
            this.getElement().removeAttribute(FORMAT_SOURCE_ATTRNAME, this.getElement().getNamespace());
-
 
129
        } else {
-
 
130
            this.getElement().setAttribute(FORMAT_SOURCE_ATTRNAME, FORMAT_SOURCE_LANG, this.getElement().getNamespace());
-
 
131
        }
-
 
132
    }
-
 
133
 
-
 
134
    public final boolean isAutomaticOrder() {
-
 
135
        return StyleProperties.parseBoolean(this.getElement().getAttributeValue(AUTOMATIC_ORDER_ATTRNAME, this.getElement().getNamespace()), false);
-
 
136
    }
-
 
137
 
-
 
138
    final void setAutomaticOrder(final boolean b) {
-
 
139
        if (b) {
-
 
140
            this.getElement().setAttribute(AUTOMATIC_ORDER_ATTRNAME, Boolean.toString(b), this.getElement().getNamespace());
-
 
141
        } else {
-
 
142
            this.getElement().removeAttribute(AUTOMATIC_ORDER_ATTRNAME, this.getElement().getNamespace());
-
 
143
        }
-
 
144
    }
-
 
145
 
101
    private final void format(final StringBuilder res, final StringBuilder pattern, final Locale styleLocale, final Calendar currentCalendar, final Date d) {
146
    private final void format(final StringBuilder res, final List<String> skeleton, final StringBuilder pattern, final Locale styleLocale, final boolean automaticOrder, final Calendar currentCalendar,
-
 
147
            final Date d) {
102
        if (pattern.length() > 0) {
148
        if (pattern.length() > 0) {
-
 
149
            if (automaticOrder) {
-
 
150
                // ask ICU the preferred order for our skeleton,
-
 
151
                // then change order of components in our pattern (thus keeping literals)
-
 
152
 
-
 
153
                // e.g. [EEEE, dd, MM, yyyy] => "EEEE, MM dd yyyy"
-
 
154
                final String bestPattern = DateTimePatternGenerator.getInstance(styleLocale).getBestPattern(CollectionUtils.join(skeleton, ""), DateTimePatternGenerator.MATCH_ALL_FIELDS_LENGTH);
-
 
155
                final SortedMap<Integer, String> bestOrder = new TreeMap<>();
-
 
156
                for (final String comp : skeleton) {
-
 
157
                    final int indexOf = bestPattern.indexOf(comp);
-
 
158
                    if (indexOf < 0)
-
 
159
                        throw new IllegalStateException("Missing " + comp + " in best pattern : " + bestPattern);
-
 
160
                    final String prev = bestOrder.put(indexOf, comp);
-
 
161
                    if (prev != null)
-
 
162
                        throw new IllegalStateException("Duplicate " + comp + " in best pattern : " + bestPattern);
-
 
163
                }
-
 
164
                assert bestOrder.size() == skeleton.size();
-
 
165
                // e.g. [EEEE, MM, dd, yyyy]
-
 
166
                final List<String> reorderedSkeleton = new ArrayList<>(bestOrder.values());
-
 
167
 
-
 
168
                int indexOfStart = 0;
-
 
169
                for (int i = 0; i < skeleton.size(); i++) {
-
 
170
                    // e.g. "dd"
-
 
171
                    final String comp = skeleton.get(i);
-
 
172
                    final int reorderedIndex = reorderedSkeleton.indexOf(comp);
-
 
173
                    assert reorderedIndex >= 0;
-
 
174
                    if (i != reorderedIndex) {
-
 
175
                        // e.g. "MM"
-
 
176
                        final String compToSwap = reorderedSkeleton.get(i);
-
 
177
 
-
 
178
                        // replace "dd" by "MM"
-
 
179
                        final int indexOfComp = pattern.indexOf(comp, indexOfStart);
-
 
180
                        pattern.replace(indexOfComp, indexOfComp + comp.length(), compToSwap);
-
 
181
                        // avoid swapping back
-
 
182
                        indexOfStart = indexOfComp + compToSwap.length();
-
 
183
                    }
-
 
184
                }
-
 
185
            }
103
            final SimpleDateFormat fmt = new SimpleDateFormat(pattern.toString(), styleLocale);
186
            final SimpleDateFormat fmt = new SimpleDateFormat(pattern.toString(), styleLocale);
104
            pattern.setLength(0);
187
            pattern.setLength(0);
105
            fmt.setCalendar((Calendar) currentCalendar.clone());
188
            fmt.setCalendar((Calendar) currentCalendar.clone());
106
            res.append(fmt.format(d));
189
            res.append(fmt.format(d));
107
        }
190
        }
Line 109... Line 192...
109
 
192
 
110
    @Override
193
    @Override
111
    public String format(Object o, CellStyle defaultStyle, boolean lenient) {
194
    public String format(Object o, CellStyle defaultStyle, boolean lenient) {
112
        final Date d = o instanceof Calendar ? ((Calendar) o).getTime() : (Date) o;
195
        final Date d = o instanceof Calendar ? ((Calendar) o).getTime() : (Date) o;
113
        final Namespace numberNS = this.getElement().getNamespace();
196
        final Namespace numberNS = this.getElement().getNamespace();
-
 
197
        final boolean automaticOrder = this.isAutomaticOrder();
-
 
198
        final boolean fixedLength = this.isFormatSourceFixed();
114
        final Locale styleLocale = this.getLocale();
199
        final Locale styleLocale = this.getLocale();
115
        final Calendar styleCalendar = Calendar.getInstance(styleLocale);
200
        final Calendar styleCalendar = Calendar.getInstance(styleLocale);
116
        final StringBuilder res = new StringBuilder();
201
        final StringBuilder res = new StringBuilder();
117
 
202
 
118
        Calendar currentCalendar = styleCalendar;
203
        Calendar currentCalendar = styleCalendar;
-
 
204
        final List<String> skeleton = automaticOrder ? new ArrayList<>() : null;
119
        final StringBuilder sb = new StringBuilder();
205
        final StringBuilder sb = new StringBuilder();
120
 
206
 
-
 
207
        final Consumer<String> addComp = (s) -> {
-
 
208
            sb.append(s);
-
 
209
            if (skeleton != null)
-
 
210
                skeleton.add(s);
-
 
211
        };
-
 
212
 
121
        @SuppressWarnings("unchecked")
213
        @SuppressWarnings("unchecked")
122
        final List<Element> children = this.getElement().getChildren();
214
        final List<Element> children = this.getElement().getChildren();
123
        for (final Element elem : children) {
215
        for (final Element elem : children) {
124
            if (elem.getNamespace().equals(numberNS)) {
216
            if (elem.getNamespace().equals(numberNS)) {
125
                final Calendar calendarLocaleElem = getCalendar(elem, styleCalendar);
217
                final Calendar calendarLocaleElem = getCalendar(elem, styleCalendar);
126
                if (!calendarLocaleElem.equals(currentCalendar)) {
218
                if (!calendarLocaleElem.equals(currentCalendar)) {
127
                    format(res, sb, styleLocale, currentCalendar, d);
219
                    format(res, skeleton, sb, styleLocale, automaticOrder, currentCalendar, d);
128
                    currentCalendar = calendarLocaleElem;
220
                    currentCalendar = calendarLocaleElem;
129
                }
221
                }
130
 
222
 
131
                if (elem.getName().equals("text")) {
223
                if (elem.getName().equals("text")) {
132
                    DataStyle.addStringLiteral(sb, elem.getText());
224
                    DataStyle.addStringLiteral(sb, elem.getText());
133
                } else if (elem.getName().equals("era")) {
225
                } else if (elem.getName().equals("era")) {
134
                    sb.append(isShort(elem) ? "G" : "GGGG");
226
                    addComp.accept(isShort(elem) ? "G" : "GGGG");
135
                } else if (elem.getName().equals("year")) {
227
                } else if (elem.getName().equals("year")) {
136
                    sb.append(isShort(elem) ? "yy" : "yyyy");
228
                    addComp.accept(isShort(elem, fixedLength, styleLocale) ? "yy" : "yyyy");
137
                } else if (elem.getName().equals("quarter")) {
229
                } else if (elem.getName().equals("quarter")) {
138
                    final Calendar cal = (Calendar) currentCalendar.clone();
230
                    final Calendar cal = (Calendar) currentCalendar.clone();
139
                    cal.setTime(d);
231
                    cal.setTime(d);
140
                    final double quarterLength = cal.getActualMaximum(Calendar.MONTH) / 4.0;
232
                    final double quarterLength = cal.getActualMaximum(Calendar.MONTH) / 4.0;
141
                    final int quarter = (int) (cal.get(Calendar.MONTH) / quarterLength + 1);
233
                    final int quarter = (int) (cal.get(Calendar.MONTH) / quarterLength + 1);
Line 146... Line 238...
146
                } else if (elem.getName().equals("month")) {
238
                } else if (elem.getName().equals("month")) {
147
                    final Attribute possessive = elem.getAttribute("possessive-form", numberNS);
239
                    final Attribute possessive = elem.getAttribute("possessive-form", numberNS);
148
                    if (possessive != null)
240
                    if (possessive != null)
149
                        reportError("Ignoring " + possessive, lenient);
241
                        reportError("Ignoring " + possessive, lenient);
150
                    if (!StyleProperties.parseBoolean(elem.getAttributeValue("textual", numberNS), false))
242
                    if (!StyleProperties.parseBoolean(elem.getAttributeValue("textual", numberNS), false))
151
                        sb.append(isShort(elem) ? "M" : "MM");
243
                        addComp.accept(isShort(elem, fixedLength, styleLocale) ? "M" : "MM");
152
                    else
244
                    else
153
                        sb.append(isShort(elem) ? "MMM" : "MMMM");
245
                        addComp.accept(isShort(elem) ? "MMM" : "MMMM");
154
                } else if (elem.getName().equals("week-of-year")) {
246
                } else if (elem.getName().equals("week-of-year")) {
155
                    sb.append("w");
247
                    addComp.accept("w");
156
                } else if (elem.getName().equals("day")) {
248
                } else if (elem.getName().equals("day")) {
157
                    sb.append(isShort(elem) ? "d" : "dd");
249
                    addComp.accept(isShort(elem, fixedLength, styleLocale) ? "d" : "dd");
158
                } else if (elem.getName().equals("day-of-week")) {
250
                } else if (elem.getName().equals("day-of-week")) {
159
                    sb.append(isShort(elem) ? "E" : "EEEE");
251
                    addComp.accept(isShort(elem, fixedLength, styleLocale) ? "E" : "EEEE");
160
                } else if (elem.getName().equals("am-pm")) {
252
                } else if (elem.getName().equals("am-pm")) {
161
                    sb.append("a");
253
                    addComp.accept("a");
162
                } else if (elem.getName().equals("hours")) {
254
                } else if (elem.getName().equals("hours")) {
163
                    // see 16.27.22 : If a <number:am-pm> element is contained in a date or time
255
                    // see 16.27.22 : If a <number:am-pm> element is contained in a date or time
164
                    // style, hours are displayed using values from 1 to 12 only.
256
                    // style, hours are displayed using values from 1 to 12 only.
165
                    if (getElement().getChild("am-pm", numberNS) == null)
257
                    if (getElement().getChild("am-pm", numberNS) == null)
166
                        sb.append(isShort(elem) ? "H" : "HH");
258
                        addComp.accept(isShort(elem) ? "H" : "HH");
167
                    else
259
                    else
168
                        sb.append(isShort(elem) ? "h" : "hh");
260
                        addComp.accept(isShort(elem) ? "h" : "hh");
169
                } else if (elem.getName().equals("minutes")) {
261
                } else if (elem.getName().equals("minutes")) {
170
                    sb.append(isShort(elem) ? "m" : "mm");
262
                    addComp.accept(isShort(elem) ? "m" : "mm");
171
                } else if (elem.getName().equals("seconds")) {
263
                } else if (elem.getName().equals("seconds")) {
172
                    sb.append(isShort(elem) ? "s" : "ss");
264
                    addComp.accept(isShort(elem) ? "s" : "ss");
173
                    final int decPlaces = StyleProperties.parseInt(elem.getAttributeValue("decimal-places", numberNS), 0);
265
                    final int decPlaces = StyleProperties.parseInt(elem.getAttributeValue("decimal-places", numberNS), 0);
174
                    if (decPlaces > 0) {
266
                    if (decPlaces > 0) {
175
                        // use styleLocale since <seconds> hasn't @calendar
267
                        // use styleLocale since <seconds> hasn't @calendar
176
                        final Calendar cal = Calendar.getInstance(styleLocale);
268
                        final Calendar cal = Calendar.getInstance(styleLocale);
177
                        cal.setTime(d);
269
                        cal.setTime(d);
Line 181... Line 273...
181
                        DataStyle.addStringLiteral(sb, fractionPart);
273
                        DataStyle.addStringLiteral(sb, fractionPart);
182
                    }
274
                    }
183
                }
275
                }
184
            }
276
            }
185
        }
277
        }
186
        format(res, sb, styleLocale, currentCalendar, d);
278
        format(res, skeleton, sb, styleLocale, automaticOrder, currentCalendar, d);
187
        return res.toString();
279
        return res.toString();
188
    }
280
    }
189
}
281
}