OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 67 | Rev 180 | Go to most recent revision | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 67 Rev 80
Line 13... Line 13...
13
 
13
 
14
 package org.openconcerto.openoffice;
14
 package org.openconcerto.openoffice;
15
 
15
 
16
import org.openconcerto.utils.FormatGroup;
16
import org.openconcerto.utils.FormatGroup;
17
import org.openconcerto.utils.TimeUtils;
17
import org.openconcerto.utils.TimeUtils;
-
 
18
import org.openconcerto.utils.XMLCalendarFormat;
18
import org.openconcerto.utils.XMLDateFormat;
19
import org.openconcerto.utils.XMLDateFormat;
19
 
20
 
20
import java.math.BigDecimal;
21
import java.math.BigDecimal;
21
import java.text.Format;
22
import java.text.Format;
22
import java.text.ParseException;
23
import java.text.ParseException;
23
import java.text.SimpleDateFormat;
24
import java.text.SimpleDateFormat;
24
import java.util.Arrays;
25
import java.util.Arrays;
25
import java.util.Calendar;
26
import java.util.Calendar;
26
import java.util.Date;
27
import java.util.Date;
27
import java.util.List;
28
import java.util.List;
-
 
29
import java.util.Locale;
-
 
30
import java.util.TimeZone;
28
 
31
 
29
import javax.xml.datatype.Duration;
32
import javax.xml.datatype.Duration;
30
 
33
 
-
 
34
import net.jcip.annotations.GuardedBy;
-
 
35
import net.jcip.annotations.Immutable;
-
 
36
 
31
/**
37
/**
32
 * A type of value, as per 16.1 "Data Types" and 6.7.1 "Variable Value Types and Values"
38
 * A type of value, as per 16.1 "Data Types" and 6.7.1 "Variable Value Types and Values"
33
 */
39
 */
-
 
40
@Immutable
34
public enum ODValueType {
41
public enum ODValueType {
35
 
42
 
36
    /**
43
    /**
37
     * Parses to {@link BigDecimal} to return the exact number.
44
     * Parses to {@link BigDecimal} to return the exact number.
38
     */
45
     */
Line 80... Line 87...
80
    },
87
    },
81
    DATE("date-value", Date.class, Calendar.class) {
88
    DATE("date-value", Date.class, Calendar.class) {
82
 
89
 
83
        @Override
90
        @Override
84
        public String format(Object o) {
91
        public String format(Object o) {
85
            final Date d = o instanceof Calendar ? ((Calendar) o).getTime() : (Date) o;
-
 
86
            return DATE_FORMAT.format(d);
92
            return formatDate(o);
87
        }
93
        }
88
 
94
 
89
        @Override
95
        @Override
90
        public Date parse(String date) {
96
        public Date parse(String date) {
91
            if (date.length() == 0)
97
            if (date.length() == 0)
92
                return null;
98
                return null;
93
            else {
99
            else {
94
                try {
100
                try {
95
                    return (Date) DATE_FORMAT.parseObject(date);
101
                    return parseDateValue(date).getTime();
96
                } catch (ParseException e) {
102
                } catch (ParseException e) {
97
                    throw new IllegalStateException("wrong date: " + date, e);
103
                    throw new IllegalStateException("wrong date: " + date, e);
98
                }
104
                }
99
            }
105
            }
100
        }
106
        }
Line 157... Line 163...
157
    }
163
    }
158
 
164
 
159
    /**
165
    /**
160
     * The name of the value attribute for this value type.
166
     * The name of the value attribute for this value type.
161
     * 
167
     * 
162
     * @return the value attribute, eg "boolean-value".
168
     * @return the value attribute, e.g. "boolean-value".
163
     */
169
     */
164
    public final String getValueAttribute() {
170
    public final String getValueAttribute() {
165
        return this.attr;
171
        return this.attr;
166
    }
172
    }
167
 
173
 
Line 177... Line 183...
177
    public abstract Object parse(String s);
183
    public abstract Object parse(String s);
178
 
184
 
179
    /**
185
    /**
180
     * The value for the value-type attribute.
186
     * The value for the value-type attribute.
181
     * 
187
     * 
182
     * @return the value for the value-type attribute, eg "float".
188
     * @return the value for the value-type attribute, e.g. "float".
183
     */
189
     */
184
    public final String getName() {
190
    public final String getName() {
185
        return this.name().toLowerCase();
191
        return this.name().toLowerCase();
186
    }
192
    }
187
 
193
 
Line 218... Line 224...
218
            return DATE;
224
            return DATE;
219
        else
225
        else
220
            return null;
226
            return null;
221
    }
227
    }
222
 
228
 
-
 
229
    static private final TimeZone UTC_TZ = TimeZone.getTimeZone("UTC");
-
 
230
    // use nulls and not (TimeZone|Locale).getDefault() so as to not need to listen to changes
-
 
231
    // LibreOffice behavior as of 4.1 is to no longer ignore explicit time zone when reading dates.
-
 
232
    @GuardedBy("ODValueType")
-
 
233
    static private DateConfig DATE_CONFIG = new DateConfig(null, null, null, Boolean.FALSE);
-
 
234
 
223
    // see http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/#isoformats
235
    // see http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/#isoformats
224
 
236
 
-
 
237
    @GuardedBy("ODValueType")
225
    static private final Format DATE_FORMAT;
238
    static private Format DATE_FORMAT, DATE_PARSER;
226
    static {
239
    static {
-
 
240
        updateFormat();
-
 
241
    }
-
 
242
 
-
 
243
    static private synchronized String formatDate(Object obj) {
-
 
244
        return DATE_FORMAT.format(obj);
-
 
245
    }
-
 
246
 
-
 
247
    static private synchronized void updateFormat() {
-
 
248
        // always remove time zone on write
-
 
249
        DATE_FORMAT = new XMLCalendarFormat(getTimeZone(false), getLocale(false));
-
 
250
        DATE_PARSER = getFormatParser(DATE_CONFIG, true);
-
 
251
    }
-
 
252
 
-
 
253
    static private synchronized SimpleDateFormat createDateFormat(final String pattern, final DateConfig dateConf) {
-
 
254
        final SimpleDateFormat res = new SimpleDateFormat(pattern, dateConf.getLocale(true));
-
 
255
        res.setTimeZone(!dateConf.isTimeZoneIgnored() ? UTC_TZ : dateConf.getTimeZone(true));
-
 
256
        return res;
-
 
257
    }
-
 
258
 
-
 
259
    static private synchronized Format getFormatParser(final DateConfig dateConf, final boolean forceCreate) {
-
 
260
        if (!forceCreate && dateConf.equals(DATE_CONFIG)) {
-
 
261
            return DATE_PARSER;
-
 
262
        } else {
-
 
263
            final Format xmlDF;
-
 
264
            if (dateConf.isTimeZoneIgnored()) {
-
 
265
                xmlDF = new XMLCalendarFormat(dateConf.getTimeZone(false), dateConf.getLocale(false));
-
 
266
            } else {
-
 
267
                xmlDF = new XMLDateFormat(UTC_TZ, null);
-
 
268
            }
227
        // first date and time so we don't loose time information on format() or parse()
269
            // first date and time so we don't loose time information on format() or parse()
228
        // MAYBE add HH':'mm':'ss,SSS for OOo 1
270
            // MAYBE add HH':'mm':'ss,SSS for OOo 1
229
        DATE_FORMAT = new FormatGroup(new XMLDateFormat(), new SimpleDateFormat("yyyy-MM-dd'T'HH':'mm':'ss"), new SimpleDateFormat("yyyy-MM-dd"));
271
            return new FormatGroup(xmlDF, createDateFormat("yyyy-MM-dd'T'HH':'mm':'ss", dateConf), createDateFormat("yyyy-MM-dd", dateConf));
-
 
272
        }
-
 
273
    }
-
 
274
 
-
 
275
    static private synchronized final void setDateConfig(final DateConfig newVal) {
-
 
276
        if (!newVal.equals(DATE_CONFIG)) {
-
 
277
            DATE_CONFIG = newVal;
-
 
278
            updateFormat();
-
 
279
        }
-
 
280
    }
-
 
281
 
-
 
282
    /**
-
 
283
     * Set the framework default time zone. Pass <code>null</code> to always use the VM default
-
 
284
     * (passing {@link TimeZone#getDefault()} would set the value once and for all and wouldn't be
-
 
285
     * changed by {@link TimeZone#setDefault(TimeZone)}).
-
 
286
     * 
-
 
287
     * @param tz the new default time zone, <code>null</code> to use the VM default.
-
 
288
     */
-
 
289
    static public synchronized final void setTimeZone(final TimeZone tz) {
-
 
290
        setDateConfig(DATE_CONFIG.setTimeZone(tz));
-
 
291
    }
-
 
292
 
-
 
293
    /**
-
 
294
     * The framework default time zone.
-
 
295
     * 
-
 
296
     * @param notNull <code>true</code> if <code>null</code> should be replaced by
-
 
297
     *        {@link TimeZone#getDefault()}.
-
 
298
     * @return the default time zone, can only be <code>null</code> if <code>notNull</code> is
-
 
299
     *         <code>false</code>.
-
 
300
     */
-
 
301
    static public synchronized final TimeZone getTimeZone(final boolean notNull) {
-
 
302
        return DATE_CONFIG.getTimeZone(notNull);
-
 
303
    }
-
 
304
 
-
 
305
    /**
-
 
306
     * Set the framework default locale. Pass <code>null</code> to always use the VM default
-
 
307
     * (passing {@link Locale#getDefault()} would set the value once and for all and wouldn't be
-
 
308
     * changed by {@link Locale#setDefault(Locale)}).
-
 
309
     * 
-
 
310
     * @param locale the new default locale, <code>null</code> to use the VM default.
-
 
311
     */
-
 
312
    static public synchronized final void setLocale(final Locale locale) {
-
 
313
        setDateConfig(DATE_CONFIG.setLocale(locale));
-
 
314
    }
-
 
315
 
-
 
316
    /**
-
 
317
     * The framework default locale.
-
 
318
     * 
-
 
319
     * @param notNull <code>true</code> if <code>null</code> should be replaced by
-
 
320
     *        {@link Locale#getDefault()}.
-
 
321
     * @return the default locale, can only be <code>null</code> if <code>notNull</code> is
-
 
322
     *         <code>false</code>.
-
 
323
     */
-
 
324
    static public synchronized final Locale getLocale(final boolean notNull) {
-
 
325
        return DATE_CONFIG.getLocale(notNull);
-
 
326
    }
-
 
327
 
-
 
328
    /**
-
 
329
     * Get the framework default calendar.
-
 
330
     * 
-
 
331
     * @return the default calendar.
-
 
332
     * @see #getTimeZone(boolean)
-
 
333
     * @see #getLocale(boolean)
-
 
334
     */
-
 
335
    static public synchronized final Calendar getCalendar() {
-
 
336
        return DATE_CONFIG.getCalendar();
-
 
337
    }
-
 
338
 
-
 
339
    static public synchronized final void setTimeZoneIgnored(final boolean b) {
-
 
340
        setDateConfig(DATE_CONFIG.setTimeZoneIgnored(b));
-
 
341
    }
-
 
342
 
-
 
343
    /**
-
 
344
     * Whether to ignore explicit time zone in dates. Prior to 4.1 LibreOffice would ignore explicit
-
 
345
     * time zones, i.e. "2013-11-15T12:00:00.000" and "2013-11-15T12:00:00.000+01:00" would both
-
 
346
     * parse to noon. As of 4.1 the first one parse to noon, the second one to 11 AM.
-
 
347
     * 
-
 
348
     * @return <code>true</code> if the time zone part should be ignored.
-
 
349
     */
-
 
350
    static public synchronized final boolean isTimeZoneIgnored() {
-
 
351
        return DATE_CONFIG.isTimeZoneIgnored();
-
 
352
    }
-
 
353
 
-
 
354
    /**
-
 
355
     * Parse an OpenDocument date value with the framework defaults.
-
 
356
     * 
-
 
357
     * @param date the string formatted value.
-
 
358
     * @return a calendar with the local time of the passed date.
-
 
359
     * @throws ParseException if the value couldn't be parsed.
-
 
360
     * @see #parseDateValue(String, TimeZone, Locale, Boolean)
-
 
361
     */
-
 
362
    static public synchronized Calendar parseDateValue(final String date) throws ParseException {
-
 
363
        return parseDateValue(date, null, null, null);
-
 
364
    }
-
 
365
 
-
 
366
    /**
-
 
367
     * Parse an OpenDocument date value with the passed parameters.
-
 
368
     * 
-
 
369
     * @param date the string formatted value.
-
 
370
     * @param tz the time zone of the returned calendar, <code>null</code> meaning
-
 
371
     *        {@link #getTimeZone(boolean)}.
-
 
372
     * @param locale the locale of the returned calendar, <code>null</code> meaning
-
 
373
     *        {@link #getLocale(boolean)}.
-
 
374
     * @param ignoreTZ whether to ignore the time zone part of <code>part</code>, <code>null</code>
-
 
375
     *        meaning {@link #isTimeZoneIgnored()}.
-
 
376
     * @return a calendar with the local time of the passed date.
-
 
377
     * @throws ParseException if the value couldn't be parsed.
-
 
378
     */
-
 
379
    static public synchronized Calendar parseDateValue(final String date, final TimeZone tz, final Locale locale, final Boolean ignoreTZ) throws ParseException {
-
 
380
        final DateConfig conf = new DateConfig(DATE_CONFIG, tz, locale, ignoreTZ);
-
 
381
        final Object parsed = getFormatParser(conf, false).parseObject(date);
-
 
382
        if (parsed instanceof Calendar) {
-
 
383
            // XMLCalendarFormat
-
 
384
            return (Calendar) parsed;
-
 
385
        } else {
-
 
386
            final Calendar res = conf.getCalendar();
-
 
387
            if (conf.isTimeZoneIgnored()) {
-
 
388
                // SimpleDateFormat
-
 
389
                res.setTime((Date) parsed);
-
 
390
                return res;
-
 
391
            } else {
-
 
392
                // XMLDateFormat or SimpleDateFormat
-
 
393
                final Calendar cal = Calendar.getInstance(UTC_TZ);
-
 
394
                cal.setTime((Date) parsed);
-
 
395
                return TimeUtils.copyLocalTime(cal, res);
-
 
396
            }
-
 
397
        }
230
    }
398
    }
231
}
399
}