OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

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

Rev 174 Rev 180
Line 11... Line 11...
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.spreadsheet;
14
 package org.openconcerto.openoffice.spreadsheet;
15
 
15
 
-
 
16
import static org.openconcerto.utils.TimeUtils.SECONDS_PER_HOUR;
-
 
17
import static org.openconcerto.utils.TimeUtils.SECONDS_PER_MINUTE;
-
 
18
 
-
 
19
import org.openconcerto.openoffice.LengthUnit;
16
import org.openconcerto.openoffice.Log;
20
import org.openconcerto.openoffice.Log;
17
import org.openconcerto.openoffice.ODDocument;
21
import org.openconcerto.openoffice.ODDocument;
18
import org.openconcerto.openoffice.ODFrame;
22
import org.openconcerto.openoffice.ODFrame;
-
 
23
import org.openconcerto.openoffice.ODPackage;
19
import org.openconcerto.openoffice.ODValueType;
24
import org.openconcerto.openoffice.ODValueType;
20
import org.openconcerto.openoffice.StyleDesc;
25
import org.openconcerto.openoffice.StyleDesc;
21
import org.openconcerto.openoffice.XMLVersion;
26
import org.openconcerto.openoffice.XMLVersion;
22
import org.openconcerto.openoffice.spreadsheet.BytesProducer.ByteArrayProducer;
27
import org.openconcerto.openoffice.spreadsheet.BytesProducer.ByteArrayProducer;
23
import org.openconcerto.openoffice.spreadsheet.BytesProducer.ImageProducer;
28
import org.openconcerto.openoffice.spreadsheet.BytesProducer.ImageProducer;
Line 27... Line 32...
27
import org.openconcerto.utils.FileUtils;
32
import org.openconcerto.utils.FileUtils;
28
import org.openconcerto.utils.TimeUtils;
33
import org.openconcerto.utils.TimeUtils;
29
import org.openconcerto.utils.TimeUtils.DurationNullsChanger;
34
import org.openconcerto.utils.TimeUtils.DurationNullsChanger;
30
import org.openconcerto.utils.Tuple2;
35
import org.openconcerto.utils.Tuple2;
31
import org.openconcerto.utils.Tuple3;
36
import org.openconcerto.utils.Tuple3;
-
 
37
import org.openconcerto.utils.cache.LRUMap;
-
 
38
import org.openconcerto.utils.cc.CachedTransformer;
32
 
39
 
33
import java.awt.Color;
40
import java.awt.Color;
34
import java.awt.Image;
41
import java.awt.Image;
35
import java.awt.Point;
42
import java.awt.Point;
36
import java.io.File;
43
import java.io.File;
37
import java.io.IOException;
44
import java.io.IOException;
-
 
45
import java.math.BigDecimal;
38
import java.text.DateFormat;
46
import java.text.DateFormat;
39
import java.text.DecimalFormat;
47
import java.text.DecimalFormat;
-
 
48
import java.text.DecimalFormatSymbols;
40
import java.text.NumberFormat;
49
import java.text.NumberFormat;
41
import java.util.Calendar;
50
import java.util.Calendar;
42
import java.util.Date;
51
import java.util.Date;
43
import java.util.List;
52
import java.util.List;
44
import java.util.Locale;
53
import java.util.Locale;
Line 57... Line 66...
57
 * @author Sylvain
66
 * @author Sylvain
58
 * @param <D> type of document
67
 * @param <D> type of document
59
 */
68
 */
60
public class MutableCell<D extends ODDocument> extends Cell<D> {
69
public class MutableCell<D extends ODDocument> extends Cell<D> {
61
 
70
 
-
 
71
    static private final CachedTransformer<Locale, DateFormat, RuntimeException> TextPDateFormat = new CachedTransformer<>(new LRUMap<>(20), (l) -> DateFormat.getDateInstance(DateFormat.DEFAULT, l),
-
 
72
            false);
-
 
73
    static private final CachedTransformer<Locale, DateFormat, RuntimeException> TextPTimeFormat = new CachedTransformer<>(new LRUMap<>(20), (l) -> DateFormat.getTimeInstance(DateFormat.DEFAULT, l),
-
 
74
            false);
-
 
75
    static private final CachedTransformer<Locale, NumberFormat, RuntimeException> TextPMinuteSecondFormat = new CachedTransformer<>(new LRUMap<>(20),
62
    static private final DateFormat TextPDateFormat = DateFormat.getDateInstance();
76
            (l) -> new DecimalFormat("00.###", DecimalFormatSymbols.getInstance(l)), false);
63
    static private final DateFormat TextPTimeFormat = DateFormat.getTimeInstance();
77
    static private final char TEXTP_SEP = ':';
-
 
78
 
-
 
79
    // HH:mm:ss.SSS
-
 
80
    static String textPDuration(final long hours, final int minutes, final BigDecimal secsAndNanos, final Locale locale) {
-
 
81
        final StringBuilder res = new StringBuilder(16);
-
 
82
        res.append(hours);
-
 
83
        res.append(TEXTP_SEP);
64
    static private final NumberFormat TextPMinuteSecondFormat = new DecimalFormat("00.###");
84
        res.append(TextPMinuteSecondFormat.get(locale).format(minutes));
-
 
85
        res.append(TEXTP_SEP);
-
 
86
        res.append(TextPMinuteSecondFormat.get(locale).format(secsAndNanos));
-
 
87
        return res.toString();
-
 
88
    }
-
 
89
 
-
 
90
    static String textPDuration(final java.time.Duration d, final Locale locale) {
-
 
91
        // -1H is treated as 23:00 in LO
-
 
92
        if (d.isNegative())
-
 
93
            throw new UnsupportedOperationException("Negative duration");
-
 
94
 
-
 
95
        final long seconds = d.getSeconds();
-
 
96
        // from Duration.toString()
-
 
97
        final long hours = seconds / SECONDS_PER_HOUR;
-
 
98
        final int minutes = (int) ((seconds % SECONDS_PER_HOUR) / SECONDS_PER_MINUTE);
-
 
99
        final int secs = (int) (seconds % SECONDS_PER_MINUTE);
-
 
100
        final BigDecimal secsAndNanos = BigDecimal.valueOf(secs).add(BigDecimal.valueOf(d.getNano()).movePointLeft(9));
-
 
101
 
-
 
102
        return textPDuration(hours, minutes, secsAndNanos, locale);
-
 
103
    }
65
 
104
 
66
    static private boolean LO_MODE = true;
105
    static private boolean LO_MODE = true;
67
    // no date part, all time part to zero
106
    // no date part, all time part to zero
68
    static private final DurationNullsChanger TIME_NULLS = new TimeUtils.DurationNullsBuilder(TimeUtils.EmptyFieldPolicy.SET_TO_ZERO).setToNull(TimeUtils.getDateFields()).build();
107
    static private final DurationNullsChanger TIME_NULLS = new TimeUtils.DurationNullsBuilder(TimeUtils.EmptyFieldPolicy.SET_TO_ZERO).setToNull(TimeUtils.getDateFields()).build();
69
 
108
 
Line 137... Line 176...
137
            this.getElement().removeAttribute(currentType.getValueAttribute(), valueNS);
176
            this.getElement().removeAttribute(currentType.getValueAttribute(), valueNS);
138
        }
177
        }
139
        // Like LO, do not generate string-value
178
        // Like LO, do not generate string-value
140
        if (type != null && type != ODValueType.STRING) {
179
        if (type != null && type != ODValueType.STRING) {
141
            // LO cells don't support the full syntax of a duration (user meta fields do)
180
            // LO cells don't support the full syntax of a duration (user meta fields do)
142
            // instead it support only values without nYnMnD
181
            // instead it support only values without nYnMnD (as does java.time.Duration)
143
            if (type == ODValueType.TIME && getTimeValueMode()) {
182
            if (type == ODValueType.TIME && getTimeValueMode() && !(val instanceof java.time.Duration)) {
144
                final Duration d = val instanceof Duration ? (Duration) val : TimeUtils.timePartToDuration((Calendar) val);
183
                final Duration d = val instanceof Duration ? (Duration) val : TimeUtils.timePartToDuration((Calendar) val);
145
                val = TIME_NULLS.apply(getODDocument().getEpoch().normalizeToHours(d));
184
                val = TIME_NULLS.apply(getODDocument().getEpoch().normalizeToHours(d));
146
            }
185
            }
147
            this.getElement().setAttribute(type.getValueAttribute(), type.format(val), valueNS);
186
            this.getElement().setAttribute(type.getValueAttribute(), type.format(val), valueNS);
148
        }
187
        }
Line 212... Line 251...
212
        obj = formatted.get2();
251
        obj = formatted.get2();
213
 
252
 
214
        if (formatted.get0() != null) {
253
        if (formatted.get0() != null) {
215
            text = formatted.get0();
254
            text = formatted.get0();
216
        } else {
255
        } else {
217
            // either there were no format or formatting failed
256
            // either there were no format, formatting failed or wasn't attempted because the data
-
 
257
            // style cannot format vt
-
 
258
 
218
            if (vt == ODValueType.FLOAT) {
259
            if (vt == ODValueType.FLOAT) {
219
                text = getODDocument().getPackage().formatNumber((Number) obj, getDefaultStyle());
260
                text = ODPackage.formatNumber((Number) obj, getDataLocale(), getDefaultStyle());
220
            } else if (vt == ODValueType.PERCENTAGE) {
261
            } else if (vt == ODValueType.PERCENTAGE) {
221
                text = getODDocument().getPackage().formatPercent((Number) obj, getDefaultStyle());
262
                text = ODPackage.formatPercent((Number) obj, getDataLocale(), getDefaultStyle());
222
            } else if (vt == ODValueType.CURRENCY) {
263
            } else if (vt == ODValueType.CURRENCY) {
223
                text = getODDocument().getPackage().formatCurrency((Number) obj, getDefaultStyle());
264
                text = ODPackage.formatCurrency((Number) obj, getDataLocale(), getDefaultStyle());
224
            } else if (vt == ODValueType.DATE) {
265
            } else if (vt == ODValueType.DATE) {
225
                final Date d;
266
                final Date d;
226
                if (obj instanceof Calendar) {
267
                if (obj instanceof Calendar) {
227
                    d = ((Calendar) obj).getTime();
268
                    d = ((Calendar) obj).getTime();
228
                } else {
269
                } else {
229
                    d = (Date) obj;
270
                    d = (Date) obj;
230
                }
271
                }
231
                text = TextPDateFormat.format(d);
272
                text = TextPDateFormat.get(getDataLocale()).format(d);
232
            } else if (vt == ODValueType.TIME) {
273
            } else if (vt == ODValueType.TIME) {
233
                if (obj instanceof Duration) {
274
                if (obj instanceof Duration) {
234
                    final Duration normalized = getODDocument().getEpoch().normalizeToHours((Duration) obj);
275
                    final Duration normalized = getODDocument().getEpoch().normalizeToHours((Duration) obj);
235
                    text = "" + normalized.getHours() + ':' + TextPMinuteSecondFormat.format(normalized.getMinutes()) + ':' + TextPMinuteSecondFormat.format(TimeUtils.getSeconds(normalized));
276
                    text = textPDuration(normalized.getHours(), normalized.getMinutes(), TimeUtils.getSeconds(normalized), getDataLocale());
-
 
277
                } else if (obj instanceof java.time.Duration) {
-
 
278
                    text = textPDuration((java.time.Duration) obj, getDataLocale());
236
                } else {
279
                } else {
237
                    text = TextPTimeFormat.format(((Calendar) obj).getTime());
280
                    text = TextPTimeFormat.get(getDataLocale()).format(((Calendar) obj).getTime());
238
                }
281
                }
239
            } else if (vt == ODValueType.BOOLEAN) {
282
            } else if (vt == ODValueType.BOOLEAN) {
240
                Locale l = null;
-
 
241
                final CellStyle s = getStyle();
-
 
242
                if (s != null) {
-
 
243
                    final DataStyle ds = s.getDataStyle();
-
 
244
                    if (ds != null)
-
 
245
                        l = ds.getLocale();
-
 
246
                }
-
 
247
                if (l == null)
-
 
248
                    l = getODDocument().getPackage().getLocale();
-
 
249
                text = BooleanStyle.toString((Boolean) obj, l, lenient);
283
                text = BooleanStyle.toString((Boolean) obj, getDataLocale(), lenient);
250
            } else if (vt == ODValueType.STRING) {
284
            } else if (vt == ODValueType.STRING) {
251
                text = obj.toString();
285
                text = obj.toString();
252
            } else {
286
            } else {
253
                throw new IllegalStateException(vt + " unknown");
287
                throw new IllegalStateException(vt + " unknown");
254
            }
288
            }
255
        }
289
        }
256
        this.setValue(vt, obj, text);
290
        this.setValue(vt, obj, text);
257
    }
291
    }
258
 
292
 
-
 
293
    /**
-
 
294
     * The locale of the data style. NOTE this doesn't evaluate the map elements with the current
-
 
295
     * cell value.
-
 
296
     * 
-
 
297
     * @return the locale of the data style, or if none, the ODPackage locale.
-
 
298
     */
-
 
299
    public final Locale getDataLocale() {
-
 
300
        return this.getDataLocale(false);
-
 
301
    }
-
 
302
 
-
 
303
    public final Locale getDataLocale(final boolean local) {
-
 
304
        Locale res = null;
-
 
305
        final CellStyle s = getStyle();
-
 
306
        if (s != null) {
-
 
307
            final DataStyle ds = s.getDataStyle();
-
 
308
            if (ds != null)
-
 
309
                res = ds.getLocale(local);
-
 
310
        }
-
 
311
        if (local || res != null)
-
 
312
            return res;
-
 
313
        return getODDocument().getPackage().getLocale();
-
 
314
    }
-
 
315
 
-
 
316
    /**
-
 
317
     * Set the locale for the data style. This is different from the locale of the
-
 
318
     * {@link CellStyle#getTextProperties() text properties}. Like LibreOffice, this set the
-
 
319
     * attributes of the main {@link DataStyle}, and all {@link #getDataStyle() mapped} ones.
-
 
320
     * 
-
 
321
     * @param locale the new locale, <code>null</code> to remove attributes.
-
 
322
     * @throws IllegalStateException if there's no {@link DataStyle}.
-
 
323
     */
-
 
324
    public final void setDataLocale(final Locale locale) throws IllegalStateException {
-
 
325
        final CellStyle s = getStyle();
-
 
326
        if (s != null) {
-
 
327
            final DataStyle ds = s.getDataStyle();
-
 
328
            if (ds != null) {
-
 
329
                ds.setLocale(locale);
-
 
330
                // LO does this, and this avoids the need for mapped styles to have a reference to
-
 
331
                // their parent.
-
 
332
                for (final Element mapElem : ds.getMapChildren()) {
-
 
333
                    s.getDataStyle(mapElem.getAttribute("apply-style-name", mapElem.getNamespace())).setLocale(locale);
-
 
334
                }
-
 
335
                return;
-
 
336
            }
-
 
337
        }
-
 
338
        throw new IllegalStateException("No data style for " + this);
-
 
339
    }
-
 
340
 
259
    // return null String if no data style exists, or if one exists but we couldn't use it
341
    // return null String if no data style exists, or if one exists but we couldn't use it
260
    private Tuple3<String, ODValueType, Object> format(Object obj, ODValueType valueType, boolean onlyCast, boolean lenient) {
342
    private Tuple3<String, ODValueType, Object> format(Object obj, ODValueType valueType, boolean onlyCast, boolean lenient) {
261
        String res = null;
343
        String res = null;
262
        try {
344
        try {
263
            final Tuple3<DataStyle, ODValueType, Object> ds = getDataStyleAndValue(obj, valueType, onlyCast);
345
            final Tuple3<DataStyle, ODValueType, Object> ds = getDataStyleAndValue(obj, valueType, onlyCast);
Line 450... Line 532...
450
 
532
 
451
    public final StyleTableCellProperties getTableCellProperties() {
533
    public final StyleTableCellProperties getTableCellProperties() {
452
        return this.getRow().getSheet().getTableCellPropertiesAt(this.getX(), this.getY());
534
        return this.getRow().getSheet().getTableCellPropertiesAt(this.getX(), this.getY());
453
    }
535
    }
454
 
536
 
-
 
537
    public final ODFrame<D> addFrame(final Number x, final Number y, final Number w, final Number h, final LengthUnit unit) {
-
 
538
        final Element elem = ODFrame.createEmpty(getNS(), x, y, w, h, unit);
-
 
539
        this.getElement().addContent(elem);
-
 
540
        return new ODFrame<>(getODDocument(), elem);
-
 
541
    }
-
 
542
 
455
    public void setImage(final File pic) throws IOException {
543
    public void setImage(final File pic) throws IOException {
456
        this.setImage(pic, false);
544
        this.setImage(pic, false);
457
    }
545
    }
458
 
546
 
459
    public void setImage(final File pic, boolean keepRatio) throws IOException {
547
    public void setImage(final File pic, boolean keepRatio) throws IOException {
Line 465... Line 553...
465
    }
553
    }
466
 
554
 
467
    private void setImage(final String name, final BytesProducer data) {
555
    private void setImage(final String name, final BytesProducer data) {
468
        final Namespace draw = this.getNS().getNS("draw");
556
        final Namespace draw = this.getNS().getNS("draw");
469
        final Element frame = this.getElement().getChild("frame", draw);
557
        final Element frame = this.getElement().getChild("frame", draw);
470
        final Element imageElem = frame == null ? null : frame.getChild("image", draw);
-
 
471
 
558
 
472
        if (imageElem != null) {
559
        if (frame != null) {
473
            final Attribute refAttr = imageElem.getAttribute("href", this.getNS().getNS("xlink"));
-
 
474
            this.getODDocument().getPackage().putFile(refAttr.getValue(), null);
560
            new ODFrame<>(getODDocument(), frame).setImage(name, data, false);
475
 
-
 
476
            if (data == null)
-
 
477
                frame.detach();
-
 
478
            else {
-
 
479
                refAttr.setValue("Pictures/" + name + (data.getFormat() != null ? "." + data.getFormat() : ""));
-
 
480
                this.getODDocument().getPackage().putFile(refAttr.getValue(), data.getBytes(new ODFrame<D>(getODDocument(), frame)));
-
 
481
            }
-
 
482
        } else if (data != null)
561
        } else if (data != null)
483
            throw new IllegalStateException("this cell doesn't contain an image: " + this);
562
            throw new IllegalStateException("this cell doesn't contain a frame: " + this);
484
    }
563
    }
485
 
564
 
486
    public final void setBackgroundColor(final Color color) {
565
    public final void setBackgroundColor(final Color color) {
487
        this.getPrivateStyle().getTableCellProperties(this).setBackgroundColor(color);
566
        this.getPrivateStyle().getTableCellProperties(this).setBackgroundColor(color);
488
    }
567
    }