OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 110 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
110 ilm 1
package org.openconcerto.modules.reports.olap.renderer;
2
 
3
import java.awt.Color;
4
import java.awt.Dimension;
5
import java.awt.Font;
6
import java.awt.Graphics;
7
import java.awt.Graphics2D;
8
import java.awt.Rectangle;
9
import java.awt.RenderingHints;
10
import java.util.AbstractList;
11
import java.util.Iterator;
12
import java.util.List;
13
 
14
import javax.swing.JComponent;
15
import javax.swing.JViewport;
16
import javax.swing.Scrollable;
17
import javax.swing.SwingConstants;
18
 
19
import org.olap4j.Cell;
20
import org.olap4j.CellSet;
21
import org.olap4j.CellSetAxis;
22
import org.olap4j.Position;
23
import org.olap4j.impl.CoordinateIterator;
24
import org.olap4j.impl.Olap4jUtil;
25
import org.olap4j.metadata.Member;
26
 
27
public class CellSetRenderer extends JComponent implements Scrollable {
28
    private static final Color BORDER_COLOR = Color.LIGHT_GRAY;
29
    private CellSet cellSet;
30
    private CellSetAxis columnsAxis;
31
    private AxisInfo columnsAxisInfo;
32
    private CellSetAxis rowsAxis;
33
    private AxisInfo rowsAxisInfo;
34
    private int w;
35
    private int h;
159 ilm 36
    private Matrix matrix;
110 ilm 37
 
38
    public CellSetRenderer() {
39
 
40
    }
41
 
159 ilm 42
    public Matrix getMatrix() {
43
        return matrix;
44
    }
45
 
110 ilm 46
    public CellSetRenderer(CellSet set) {
47
        this.cellSet = set;
48
        computeCells();
49
    }
50
 
51
    public void setCellSet(CellSet set) {
159 ilm 52
        this.matrix = null;
110 ilm 53
        this.cellSet = set;
54
        computeCells();
55
        repaint();
56
        this.setSize(new Dimension(w, h));
57
    }
58
 
159 ilm 59
    public void setMatrix(Matrix matrix2) {
60
        this.matrix = matrix2;
61
        this.cellSet = null;
62
        this.rowsAxis = null;
63
        this.rowsAxisInfo = null;
64
        this.columnsAxis = null;
65
        this.columnsAxisInfo = null;
66
        repaint();
67
        this.setSize(new Dimension(w, h));
68
    }
69
 
110 ilm 70
    private void computeCells() {
71
        if (cellSet == null) {
72
            return;
73
        }
74
 
75
        // Compute how many rows are required to display the columns axis.
76
        // In the example, this is 4 (1997, Q1, space, Unit Sales)
77
 
78
        if (cellSet.getAxes().size() > 0) {
79
            columnsAxis = cellSet.getAxes().get(0);
80
        } else {
81
            columnsAxis = null;
82
        }
83
        columnsAxisInfo = computeAxisInfo(columnsAxis);
84
 
85
        // Compute how many columns are required to display the rows axis.
86
        // In the example, this is 3 (the width of USA, CA, Los Angeles)
87
 
88
        if (cellSet.getAxes().size() > 1) {
89
            rowsAxis = cellSet.getAxes().get(1);
90
        } else {
91
            rowsAxis = null;
92
        }
93
        rowsAxisInfo = computeAxisInfo(rowsAxis);
94
 
95
    }
96
 
97
    boolean isOuside(int x, int y) {
159 ilm 98
        if (rowsAxisInfo == null) {
99
            return false;
100
        }
110 ilm 101
        return x < rowsAxisInfo.getWidth() && y < columnsAxisInfo.getWidth();
102
    }
103
 
104
    boolean isValue(int x, int y) {
159 ilm 105
        if (rowsAxisInfo == null) {
106
            return true;
107
        }
110 ilm 108
        return x >= rowsAxisInfo.getWidth() && y >= columnsAxisInfo.getWidth();
109
    }
110
 
111
    @Override
112
    protected void paintComponent(Graphics g) {
113
        final Rectangle clipBounds = g.getClipBounds();
114
 
115
        if (g instanceof Graphics2D) {
116
            Graphics2D g2 = (Graphics2D) g;
117
            g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
118
        }
119
 
120
        g.setColor(Color.WHITE);
121
        g.fillRect(clipBounds.x, clipBounds.y, clipBounds.width, clipBounds.height);
159 ilm 122
 
123
        if (cellSet == null && matrix == null) {
110 ilm 124
            return;
125
        }
126
 
159 ilm 127
        if (cellSet != null && cellSet.getAxes().size() > 2) {
110 ilm 128
            int[] dimensions = new int[cellSet.getAxes().size() - 2];
129
            for (int i = 2; i < cellSet.getAxes().size(); i++) {
130
                CellSetAxis cellSetAxis = cellSet.getAxes().get(i);
131
                dimensions[i - 2] = cellSetAxis.getPositions().size();
132
            }
133
            for (int[] pageCoords : CoordinateIterator.iterate(dimensions)) {
134
                formatPage(g, pageCoords, columnsAxis, columnsAxisInfo, rowsAxis, rowsAxisInfo);
135
            }
136
        } else {
137
            formatPage(g, new int[] {}, columnsAxis, columnsAxisInfo, rowsAxis, rowsAxisInfo);
138
        }
139
    }
140
 
141
    private void formatPage(Graphics g, int[] pageCoords, CellSetAxis columnsAxis, AxisInfo columnsAxisInfo, CellSetAxis rowsAxis, AxisInfo rowsAxisInfo) {
142
        if (pageCoords.length > 0) {
143
 
144
            for (int i = pageCoords.length - 1; i >= 0; --i) {
145
                int pageCoord = pageCoords[i];
146
                final CellSetAxis axis = cellSet.getAxes().get(2 + i);
147
                System.out.print(axis.getAxisOrdinal() + ": ");
148
                final Position position = axis.getPositions().get(pageCoord);
149
                int k = -1;
150
                for (Member member : position.getMembers()) {
151
                    if (++k > 0) {
152
                        System.out.print(", ");
153
                    }
154
                    System.out.print(member.getUniqueName());
155
                }
156
                System.out.println();
157
            }
158
        }
159
        // Figure out the dimensions of the blank rectangle in the top left
160
        // corner.
159 ilm 161
        int yOffset = 0;
162
        int xOffsset = 0;
110 ilm 163
 
164
        // Populate a string matrix
159 ilm 165
        if (matrix == null) {
166
            yOffset = columnsAxisInfo.getWidth();
167
            xOffsset = rowsAxisInfo.getWidth();
168
            this.matrix = new Matrix(xOffsset + (columnsAxis == null ? 1 : columnsAxis.getPositions().size()), yOffset + (rowsAxis == null ? 1 : rowsAxis.getPositions().size()));
110 ilm 169
 
159 ilm 170
            // Populate corner
171
            for (int x = 0; x < xOffsset; x++) {
172
                for (int y = 0; y < yOffset; y++) {
173
                    matrix.set(x, y, "", false, x > 0);
174
                }
110 ilm 175
            }
176
 
159 ilm 177
            // Populate matrix with cells representing axes
178
            // noinspection SuspiciousNameCombination
179
            populateAxis(matrix, columnsAxis, columnsAxisInfo, true, xOffsset);
180
            populateAxis(matrix, rowsAxis, rowsAxisInfo, false, yOffset);
110 ilm 181
 
159 ilm 182
            // Populate cell values
183
            for (Cell cell : cellIter(pageCoords, cellSet)) {
184
                final List<Integer> coordList = cell.getCoordinateList();
185
                int x = xOffsset;
186
                if (coordList.size() > 0) {
187
                    x += coordList.get(0);
188
                }
189
                int y = yOffset;
190
                if (coordList.size() > 1) {
191
                    y += coordList.get(1);
192
                }
193
                matrix.set(x, y, cell.getFormattedValue(), true, false);
110 ilm 194
            }
195
        }
196
        final int matrixWidth = matrix.getWidth();
197
        final int matrixHeight = matrix.getHeight();
198
        int[] columnWidths = new int[matrixWidth];
199
        int widestWidth = 0;
200
        int offsetX = 0;
201
        int spaceW = (int) g.getFontMetrics().getStringBounds("A", g).getWidth();
202
        int spaceH = (int) g.getFontMetrics().getStringBounds("A", g).getHeight();
203
        final int lineHeight = 2 * spaceH;
204
        Font boldFont = g.getFont().deriveFont(Font.BOLD);
205
        Font normalFont = g.getFont();
206
 
207
        final Rectangle clipBounds = g.getClipBounds();
208
 
209
        for (int x = 0; x < matrixWidth; x++) {
210
            int columnWidth = 0;
211
            for (int y = 0; y < matrixHeight; y++) {
212
                MatrixCell cell = matrix.get(x, y);
213
 
214
                if (cell != null) {
215
                    if (isValue(x, y)) {
216
                        g.setFont(normalFont);
217
                    } else {
218
                        g.setFont(boldFont);
219
                    }
220
 
221
                    int w = (int) g.getFontMetrics().getStringBounds(cell.value, g).getWidth() + 2 * spaceW;
222
                    columnWidth = Math.max(columnWidth, w);
223
                }
224
            }
225
            columnWidths[x] = columnWidth;
226
 
227
            for (int y = 0; y < matrixHeight; y++) {
228
                if ((y + 1) * lineHeight < clipBounds.y) {
229
                    continue;
230
                }
231
                if (y * lineHeight > clipBounds.y + clipBounds.height) {
232
                    break;
233
                }
234
 
235
                MatrixCell cell = matrix.get(x, y);
236
                if (cell != null) {
237
                    if (isValue(x, y)) {
238
                        g.setColor(Color.WHITE);
239
                        g.fillRect(offsetX, y * lineHeight, columnWidth, lineHeight);
240
                    } else if (isOuside(x, y)) {
241
                        g.setColor(Color.WHITE);
242
                        g.fillRect(offsetX, y * lineHeight, columnWidth, lineHeight);
243
                    } else {
244
                        g.setColor(new Color(240, 240, 240));
245
                        g.fillRect(offsetX, y * lineHeight, columnWidth, lineHeight);
246
                    }
247
 
248
                    String value = cell.value;
249
                    // value += "(" + x + "," + y + ")" + isValue(x, y) + ":" + isOuside(x, y);
250
                    if (value.length() > 0) {
251
                        g.setColor(Color.BLACK);
252
 
253
                        int xString = offsetX + spaceW;
254
                        if (isValue(x, y)) {
255
                            g.setFont(normalFont);
256
                            int w = (int) g.getFontMetrics().getStringBounds(value, g).getWidth();
257
                            xString = offsetX + columnWidth - w - spaceW;
258
 
259
                        } else {
260
                            g.setFont(boldFont);
261
                        }
262
 
263
                        g.drawString(value, xString, y * lineHeight + (int) (spaceH * 1.4f));
264
                    }
159 ilm 265
                    if (columnsAxisInfo != null) {
266
                        // Top
267
                        if (!isOuside(x, y)
268
                                && (value.length() > 0 || y == columnsAxisInfo.getWidth() || isValue(x, y) || (!isValue(x, y) && (leftNotEmpty(matrix, x, y - 1)) || leftNotEmpty(matrix, x, y)))) {
269
                            g.setColor(BORDER_COLOR);
270
                            g.drawLine(offsetX, y * lineHeight, offsetX + columnWidth, y * lineHeight);
110 ilm 271
 
159 ilm 272
                        }
273
 
274
                        if (rowsAxisInfo != null) {
275
                            if ((y < columnsAxisInfo.getWidth() && x >= rowsAxisInfo.getWidth()) || isValue(x, y) || (!isOuside(x, y) && (!leftNotEmpty(matrix, x, y) || !isValue(x, y)))) {
276
                                g.setColor(BORDER_COLOR);
277
                                // Left
278
                                g.drawLine(offsetX, y * lineHeight, offsetX, (y + 1) * lineHeight);
279
                            }
280
 
281
                        }
282
                    } else {
110 ilm 283
                        g.setColor(BORDER_COLOR);
284
                        g.drawLine(offsetX, y * lineHeight, offsetX + columnWidth, y * lineHeight);
285
                        g.drawLine(offsetX, y * lineHeight, offsetX, (y + 1) * lineHeight);
286
                    }
159 ilm 287
                }
110 ilm 288
 
289
            }
290
            offsetX += columnWidth;
291
            widestWidth = Math.max(columnWidth, widestWidth);
292
        }
293
        // Right
294
        g.setColor(BORDER_COLOR);
159 ilm 295
 
110 ilm 296
        final int bottomRightX = offsetX;
297
        final int bottomRightY = matrixHeight * lineHeight;
298
        g.drawLine(offsetX, 0, offsetX, bottomRightY);
299
        // Bottom
300
        g.drawLine(0, bottomRightY, offsetX, bottomRightY);
301
 
302
        this.w = bottomRightX + 1;
303
        this.h = bottomRightY + 1;
304
 
305
    }
306
 
307
    private boolean leftNotEmpty(Matrix matrix, int x, int y) {
308
        if (y < 0) {
309
            return true;
310
        }
311
        for (int i = 0; i < x; i++) {
312
            final MatrixCell matrixCell = matrix.get(i, y);
313
            if (matrixCell != null && !matrixCell.value.isEmpty()) {
314
                return true;
315
            }
316
 
317
        }
318
        return false;
319
    }
320
 
321
    @Override
322
    public Dimension getPreferredSize() {
323
        return new Dimension(w, h);
324
    }
325
 
326
    @Override
327
    public Dimension getMinimumSize() {
328
        return new Dimension(w, h);
329
    }
330
 
331
    /**
332
     * Computes a description of an axis.
333
     *
334
     * @param axis Axis
335
     * @return Description of axis
336
     */
337
    private AxisInfo computeAxisInfo(CellSetAxis axis) {
338
        if (axis == null) {
339
            return new AxisInfo(0);
340
        }
341
        final AxisInfo axisInfo = new AxisInfo(axis.getAxisMetaData().getHierarchies().size());
342
        int p = -1;
343
        for (Position position : axis.getPositions()) {
344
            ++p;
345
            int k = -1;
346
            for (Member member : position.getMembers()) {
347
                ++k;
348
                final AxisOrdinalInfo axisOrdinalInfo = axisInfo.ordinalInfos.get(k);
349
                final int topDepth = member.isAll() ? member.getDepth() : member.getHierarchy().hasAll() ? 1 : 0;
350
                if (axisOrdinalInfo.minDepth > topDepth || p == 0) {
351
                    axisOrdinalInfo.minDepth = topDepth;
352
                }
353
                axisOrdinalInfo.maxDepth = Math.max(axisOrdinalInfo.maxDepth, member.getDepth());
354
            }
355
        }
356
        axisInfo.computeWidth();
357
        return axisInfo;
358
    }
359
 
360
    /**
361
     * Populates cells in the matrix corresponding to a particular axis.
362
     *
363
     * @param matrix Matrix to populate
364
     * @param axis Axis
365
     * @param axisInfo Description of axis
366
     * @param isColumns True if columns, false if rows
367
     * @param offset Ordinal of first cell to populate in matrix
368
     */
369
    private void populateAxis(Matrix matrix, CellSetAxis axis, AxisInfo axisInfo, boolean isColumns, int offset) {
370
        if (axis == null) {
371
            return;
372
        }
373
        Member[] prevMembers = new Member[axisInfo.getWidth()];
374
        Member[] members = new Member[axisInfo.getWidth()];
375
        for (int i = 0; i < axis.getPositions().size(); i++) {
376
            final int x = offset + i;
377
            Position position = axis.getPositions().get(i);
378
            int yOffset = 0;
379
            final List<Member> memberList = position.getMembers();
380
            for (int j = 0; j < memberList.size(); j++) {
381
                Member member = memberList.get(j);
382
                final AxisOrdinalInfo ordinalInfo = axisInfo.ordinalInfos.get(j);
383
                while (member != null) {
384
                    if (member.getDepth() < ordinalInfo.minDepth) {
385
                        break;
386
                    }
387
                    final int y = yOffset + member.getDepth() - ordinalInfo.minDepth;
388
                    members[y] = member;
389
                    member = member.getParentMember();
390
                }
391
                yOffset += ordinalInfo.getWidth();
392
            }
393
            boolean same = true;
394
            for (int y = 0; y < members.length; y++) {
395
                Member member = members[y];
396
                same = same && i > 0 && Olap4jUtil.equal(prevMembers[y], member);
397
                String value = member == null ? "" : member.getCaption();
398
                if (isColumns) {
399
                    matrix.set(x, y, value, false, same);
400
                } else {
401
                    if (same) {
402
                        value = "";
403
                    }
404
                    // noinspection SuspiciousNameCombination
405
                    matrix.set(y, x, value, false, false);
406
                }
407
                prevMembers[y] = member;
408
                members[y] = null;
409
            }
410
        }
411
    }
412
 
413
    /**
414
     * Returns an iterator over cells in a result.
415
     */
416
    private static Iterable<Cell> cellIter(final int[] pageCoords, final CellSet cellSet) {
417
        return new Iterable<Cell>() {
418
            public Iterator<Cell> iterator() {
419
                int[] axisDimensions = new int[cellSet.getAxes().size() - pageCoords.length];
420
                assert pageCoords.length <= axisDimensions.length;
421
                for (int i = 0; i < axisDimensions.length; i++) {
422
                    CellSetAxis axis = cellSet.getAxes().get(i);
423
                    axisDimensions[i] = axis.getPositions().size();
424
                }
425
                final CoordinateIterator coordIter = new CoordinateIterator(axisDimensions, true);
426
                return new Iterator<Cell>() {
427
                    public boolean hasNext() {
428
                        return coordIter.hasNext();
429
                    }
430
 
431
                    public Cell next() {
432
                        final int[] ints = coordIter.next();
433
                        final AbstractList<Integer> intList = new AbstractList<Integer>() {
434
                            public Integer get(int index) {
435
                                return index < ints.length ? ints[index] : pageCoords[index - ints.length];
436
                            }
437
 
438
                            public int size() {
439
                                return pageCoords.length + ints.length;
440
                            }
441
                        };
442
                        return cellSet.getCell(intList);
443
                    }
444
 
445
                    public void remove() {
446
                        throw new UnsupportedOperationException();
447
                    }
448
                };
449
            }
450
        };
451
    }
452
 
453
    @Override
454
    public Dimension getPreferredScrollableViewportSize() {
455
        return this.getPreferredSize();
456
    }
457
 
458
    @Override
459
    public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
460
        return 32;
461
    }
462
 
463
    @Override
464
    public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
465
        return (orientation == SwingConstants.VERTICAL) ? visibleRect.height : visibleRect.width;
466
    }
467
 
468
    @Override
469
    public boolean getScrollableTracksViewportWidth() {
470
        if (getParent() instanceof JViewport) {
471
            return (((JViewport) getParent()).getWidth() > getPreferredSize().width);
472
        }
473
        return false;
474
    }
475
 
476
    @Override
477
    public boolean getScrollableTracksViewportHeight() {
478
        if (getParent() instanceof JViewport) {
479
            return (((JViewport) getParent()).getHeight() > getPreferredSize().height);
480
        }
481
        return false;
482
    }
483
 
484
}