OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 17 | Rev 20 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
17 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.spreadsheet;
15
 
16
import org.openconcerto.openoffice.ODDocument;
17
import org.openconcerto.openoffice.ODFrame;
18
import org.openconcerto.openoffice.ODValueType;
19
import org.openconcerto.openoffice.spreadsheet.BytesProducer.ByteArrayProducer;
20
import org.openconcerto.openoffice.spreadsheet.BytesProducer.ImageProducer;
21
import org.openconcerto.utils.FileUtils;
22
 
23
import java.awt.Color;
24
import java.awt.Image;
25
import java.io.File;
26
import java.io.IOException;
27
import java.text.DateFormat;
28
import java.text.DecimalFormat;
29
import java.text.NumberFormat;
30
import java.text.SimpleDateFormat;
31
import java.util.Date;
32
import java.util.List;
33
 
34
import org.jdom.Attribute;
35
import org.jdom.Element;
36
import org.jdom.Namespace;
37
import org.jdom.Text;
38
 
39
/**
40
 * A cell whose value can be changed.
41
 *
42
 * @author Sylvain
43
 * @param <D> type of document
44
 */
45
public class MutableCell<D extends ODDocument> extends Cell<D> {
46
 
47
    static private final DateFormat TextPDateFormat = new SimpleDateFormat("dd/MM/yyyy");
48
    static private final NumberFormat TextPFloatFormat = new DecimalFormat(",##0.00");
49
 
50
    MutableCell(Row<D> parent, Element elem) {
51
        super(parent, elem);
52
    }
53
 
54
    // ask our column to our row so we don't have to update anything when columns are removed/added
55
    public final int getX() {
56
        return this.getRow().getX(this);
57
    }
58
 
59
    public final int getY() {
60
        return this.getRow().getY();
61
    }
62
 
63
    // *** setValue
64
 
65
    private void setValueAttributes(ODValueType type, Object val) {
66
        if (type == null) {
67
            final Attribute valueTypeAttr = this.getElement().getAttribute("value-type", getValueNS());
68
            if (valueTypeAttr != null) {
69
                valueTypeAttr.detach();
70
                this.getElement().removeAttribute(ODValueType.get(valueTypeAttr.getValue()).getValueAttribute(), getValueNS());
71
            }
72
        } else {
73
            this.getElement().setAttribute("value-type", type.getName(), getValueNS());
74
            this.getElement().setAttribute(type.getValueAttribute(), type.format(val), getValueNS());
75
        }
76
    }
77
 
78
    // ATTN this removes any content associated with this cell be it notes, cell anchored objects,
79
    // etc. This is because it's difficult to tell apart the text content and the rest (e.g. notes),
80
    // for example in Calc office:annotation is a child of table:cell whereas in Writer it's a child
81
    // of text:p.
82
    private void setTextP(String value) {
83
        if (value == null)
84
            this.getElement().removeContent();
85
        else {
86
            // try to reuse the first text:p to keep style
87
            final Element child = this.getElement().getChild("p", getNS().getTEXT());
88
            final Element t = child != null ? child : new Element("p", getNS().getTEXT());
89
            t.setContent(new Text(value));
90
 
91
            this.getElement().setContent(t);
92
        }
93
    }
94
 
95
    private void setValue(ODValueType type, Object value, String textP) {
96
        this.setValueAttributes(type, value);
97
        this.setTextP(textP);
98
    }
99
 
100
    public void clearValue() {
101
        this.setValue(null, null, null);
102
    }
103
 
104
    public void setValue(Object obj) {
105
        // FIXME use arbitrary textP format, should use the cell format
106
        // TODO handle all type of objects as in ODUserDefinedMeta
107
        // setValue(Object o, final ODValueType vt)
108
        if (obj instanceof Number)
109
            // 5.2
19 ilm 110
            // FIXME voir avec Sylvain : probleme avec le viewer si Integer ou Long le textp ne doit
111
            // avoir de décimal
112
            if (obj instanceof Integer || obj instanceof Long) {
113
                this.setValue(ODValueType.FLOAT, obj, (obj == null) ? "" : obj.toString());
114
            } else {
115
                this.setValue(ODValueType.FLOAT, obj, TextPFloatFormat.format(obj));
116
            }
17 ilm 117
        else if (obj instanceof Date)
118
            this.setValue(ODValueType.DATE, obj, TextPDateFormat.format(obj));
119
        else
120
            this.setValue(null, null, obj.toString());
121
    }
122
 
123
    public void replaceBy(String oldValue, String newValue) {
124
        replaceContentBy(this.getElement(), oldValue, newValue);
125
    }
126
 
127
    private void replaceContentBy(Element l, String oldValue, String newValue) {
128
        final List content = l.getContent();
129
        for (int i = 0; i < content.size(); i++) {
130
            final Object obj = content.get(i);
131
            if (obj instanceof Text) {
132
                // System.err.println(" Text --> " + obj.toString());
133
                final Text t = (Text) obj;
134
                t.setText(t.getText().replaceAll(oldValue, newValue));
135
            } else if (obj instanceof Element) {
136
                replaceContentBy((Element) obj, oldValue, newValue);
137
            }
138
        }
139
    }
140
 
141
    public final void unmerge() {
142
        // from 8.1.3 Table Cell : table-cell are like covered-table-cell with some extra
143
        // optional attributes so it's safe to rename covered cells into normal ones
144
        final int x = this.getX();
145
        final int y = this.getY();
146
        final int columnsSpanned = getColumnsSpanned();
147
        final int rowsSpanned = getRowsSpanned();
148
        for (int i = 0; i < columnsSpanned; i++) {
149
            for (int j = 0; j < rowsSpanned; j++) {
150
                // don't mind if we change us at 0,0 we're already a table-cell
151
                this.getRow().getSheet().getImmutableCellAt(x + i, y + j).getElement().setName("table-cell");
152
            }
153
        }
154
        this.getElement().removeAttribute("number-columns-spanned", getNS().getTABLE());
155
        this.getElement().removeAttribute("number-rows-spanned", getNS().getTABLE());
156
    }
157
 
158
    /**
159
     * Merge this cell and the following ones. If this cell already spanned multiple columns/rows
160
     * this method un-merge any additional cells.
161
     *
162
     * @param columnsSpanned number of columns to merge.
163
     * @param rowsSpanned number of rows to merge.
164
     */
165
    public final void merge(final int columnsSpanned, final int rowsSpanned) {
166
        final int currentCols = this.getColumnsSpanned();
167
        final int currentRows = this.getRowsSpanned();
168
 
169
        // nothing to do
170
        if (columnsSpanned == currentCols && rowsSpanned == currentRows)
171
            return;
172
 
173
        final int x = this.getX();
174
        final int y = this.getY();
175
 
176
        // check for problems before any modifications
177
        for (int i = 0; i < columnsSpanned; i++) {
178
            for (int j = 0; j < rowsSpanned; j++) {
179
                final boolean coveredByThis = i < currentCols && j < currentRows;
180
                if (!coveredByThis) {
181
                    final int x2 = x + i;
182
                    final int y2 = y + j;
183
                    final Cell<D> immutableCell = this.getRow().getSheet().getImmutableCellAt(x2, y2);
184
                    // check for overlapping range from inside
185
                    if (immutableCell.coversOtherCells())
186
                        throw new IllegalArgumentException("Cell at " + x2 + "," + y2 + " is a merged cell.");
187
                    // and outside
188
                    if (immutableCell.getElement().getName().equals("covered-table-cell"))
189
                        throw new IllegalArgumentException("Cell at " + x2 + "," + y2 + " is already covered.");
190
                }
191
            }
192
        }
193
 
194
        final boolean shrinks = columnsSpanned < currentCols || rowsSpanned < currentRows;
195
        if (shrinks)
196
            this.unmerge();
197
 
198
        // from 8.1.3 Table Cell : table-cell are like covered-table-cell with some extra
199
        // optional attributes so it's safe to rename
200
        for (int i = 0; i < columnsSpanned; i++) {
201
            for (int j = 0; j < rowsSpanned; j++) {
202
                final boolean coveredByThis = i < currentCols && j < currentRows;
203
                // don't cover this,
204
                // if we grow the current covered cells are invalid so don't try to access them
205
                if ((i != 0 || j != 0) && (shrinks || !coveredByThis))
206
                    // MutableCell is needed to break repeated
207
                    this.getRow().getSheet().getCellAt(x + i, y + j).getElement().setName("covered-table-cell");
208
            }
209
        }
210
        this.getElement().setAttribute("number-columns-spanned", columnsSpanned + "", getNS().getTABLE());
211
        this.getElement().setAttribute("number-rows-spanned", rowsSpanned + "", getNS().getTABLE());
212
    }
213
 
214
    @Override
215
    public final String getStyleName() {
216
        return this.getRow().getSheet().getStyleNameAt(this.getX(), this.getY());
217
    }
218
 
219
    public void setImage(final File pic) throws IOException {
220
        this.setImage(pic, false);
221
    }
222
 
223
    public void setImage(final File pic, boolean keepRatio) throws IOException {
224
        this.setImage(pic.getName(), new ByteArrayProducer(FileUtils.readBytes(pic), keepRatio));
225
    }
226
 
227
    public void setImage(final String name, final Image img) throws IOException {
228
        this.setImage(name, img == null ? null : new ImageProducer(img, true));
229
    }
230
 
231
    private void setImage(final String name, final BytesProducer data) {
232
        final Namespace draw = this.getNS().getNS("draw");
233
        final Element frame = this.getElement().getChild("frame", draw);
234
        final Element imageElem = frame == null ? null : frame.getChild("image", draw);
235
 
236
        if (imageElem != null) {
237
            final Attribute refAttr = imageElem.getAttribute("href", this.getNS().getNS("xlink"));
238
            this.getODDocument().getPackage().putFile(refAttr.getValue(), null);
239
 
240
            if (data == null)
241
                frame.detach();
242
            else {
243
                refAttr.setValue("Pictures/" + name + (data.getFormat() != null ? "." + data.getFormat() : ""));
244
                this.getODDocument().getPackage().putFile(refAttr.getValue(), data.getBytes(new ODFrame<D>(getODDocument(), frame)));
245
            }
246
        } else if (data != null)
247
            throw new IllegalStateException("this cell doesn't contain an image: " + this);
248
    }
249
 
250
    public final void setBackgroundColor(final Color color) {
251
        this.getPrivateStyle().getTableCellProperties().setBackgroundColor(color);
252
    }
253
}