001: package com.calipso.reportgenerator.common;
002:
003: import javax.swing.table.AbstractTableModel;
004: import java.util.Collection;
005: import java.util.Iterator;
006: import java.util.List;
007: import java.util.Vector;
008:
009: /**
010: * Modelo para las tablas de encabezados de filas y columnas
011: */
012:
013: public class HeaderTableModel extends AbstractTableModel {
014: public static final int MODE_ROW = 0;
015: public static final int MODE_COLUMN = 1;
016: private DimensionValueNode dimensionValueNode;
017: private boolean withTotals;
018: private int dimensionCount;
019: private int mode;
020: private int rowCount;
021: private List visibleMetrics;
022: private ReportQuery reportQuery;
023: private ReportSpec reportSpec;
024: private int iterations = 0;
025:
026: /**
027: * Crea un nuevo modelo inicializandolo para la tabla de encabezados de fila.
028: * @param dimensionValueNode
029: * @param withTotals
030: * @param rowCount
031: * @param reportQuery
032: * @return
033: */
034: public static HeaderTableModel newRowHeaderTableModel(
035: DimensionValueNode dimensionValueNode, boolean withTotals,
036: int rowCount, ReportQuery reportQuery, ReportSpec reportSpec) {
037: return new HeaderTableModel(dimensionValueNode, withTotals,
038: MODE_ROW, rowCount, reportQuery, reportSpec);
039: }
040:
041: /**
042: * Crea un nuevo modelo inicializandolo para la tabla de encabezados de columna.
043: * @param dimensionValueNode
044: * @param withTotals
045: * @param rowCount
046: * @param reportQuery
047: * @return
048: */
049: public static HeaderTableModel newColumnHeaderTableModel(
050: DimensionValueNode dimensionValueNode, boolean withTotals,
051: int rowCount, ReportQuery reportQuery, ReportSpec reportSpec) {
052: return new HeaderTableModel(dimensionValueNode, withTotals,
053: MODE_COLUMN, rowCount, reportQuery, reportSpec);
054: }
055:
056: /**
057: * Crea e inicializa una nueva instancia.
058: * @param dimensionValueNode
059: * @param withTotals
060: * @param mode
061: * @param rowCount
062: * @param reportQuery
063: */
064:
065: protected HeaderTableModel(DimensionValueNode dimensionValueNode,
066: boolean withTotals, int mode, int rowCount,
067: ReportQuery reportQuery, ReportSpec reportSpec) {
068: this .dimensionValueNode = dimensionValueNode;
069: this .withTotals = withTotals;
070: this .dimensionCount = dimensionValueNode.getDimensionCount();
071: this .mode = mode;
072: this .rowCount = rowCount;
073: this .reportQuery = reportQuery;
074: this .reportSpec = reportSpec;
075: initialize();
076: }
077:
078: /**
079: * Inicializa la lista de metricas visibles a partir de la ReportQuery
080: */
081: private void initialize() {
082: visibleMetrics = reportQuery.getVisibleMetrics();
083: }
084:
085: /**
086: * Devuelve la lista de Métricas visibles
087: * @return
088: */
089: public List getVisibleMetrics() {
090: return visibleMetrics;
091: }
092:
093: /**
094: * Devuelve la cantidad de métricas visibles
095: * @return
096: */
097: public int getVisibleMetricsCount() {
098: return getVisibleMetrics().size();
099: }
100:
101: /**
102: * Devuelve el Nodo raíz del arbol de grupos
103: * @see com.calipso.reportgenerator.common.DimensionValueNode
104: * @see com.calipso.reportgenerator.reportmanager.DimensionValueTreeBuilder
105: * @return
106: */
107: public DimensionValueNode getDimensionValueNode() {
108: return dimensionValueNode;
109: }
110:
111: /**
112: * Devuelve verdadero si se deben mostrar los totales
113: * @return
114: */
115: public boolean getWithTotals() {
116: return withTotals;
117: }
118:
119: /**
120: * Especifica si se deben mostrar los totales
121: * @param withTotals
122: */
123:
124: public void setWithTotals(boolean withTotals) {
125: this .withTotals = withTotals;
126: }
127:
128: /**
129: * Asigna el nodo raíz del arbol de grupos
130: * @param dimensionValueNode
131: */
132: public void setDimensionValueNode(
133: DimensionValueNode dimensionValueNode) {
134: this .dimensionValueNode = dimensionValueNode;
135: }
136:
137: /**
138: * Devuelve la cantidad de dimensiones que agrupan
139: * @return
140: */
141: public int getDimensionCount() {
142: return dimensionCount;
143: }
144:
145: /**
146: * Asigna la cantidad de dimensiones que agrupan
147: * @param dimensionCount
148: */
149: public void setDimensionCount(int dimensionCount) {
150: this .dimensionCount = dimensionCount;
151: }
152:
153: /**
154: * Devuelve la cantidad de renglones que tendrá la tabla, su valor depennde del modo en que esté trabajando (fila o
155: * columna)
156: * @return
157: */
158: public int getRowCount() {
159: int count = 0;
160: switch (mode) {
161: case MODE_ROW:
162: return Math.max(doGetRowCount(), 1);
163: case MODE_COLUMN:
164: count = doGetColumnCount();
165: if ((getVisibleMetricsCount() > 1) || (count == 0)) {
166: count++;
167: }
168: return count;
169: }
170: return count;
171: }
172:
173: /**
174: * Devuelve la cantidad de columnas que tendrá la tabla, su valor depennde del modo en que esté trabajando (fila o
175: * columna)
176: */
177:
178: public int getColumnCount() {
179: switch (mode) {
180: case MODE_ROW:
181: return Math.max(doGetColumnCount(), 1);
182: case MODE_COLUMN:
183: return doGetRowCount();
184: }
185: return 0;
186: }
187:
188: /**
189: * devuelve la cantidad de nodos del ultimo nivel del arbol dependiendo del modo se utiliza para obtener la cantidad de
190: * filas o la cantidad de columnas del arbol
191: * @return
192: */
193: public int doGetRowCount() {
194: return rowCount;
195: }
196:
197: /**
198: * Asigna la cantidad de nodos del ultimo nivel, este valor se setea siempre que se redibuje la tabla porque si hay grupos
199: * colapsados este valor puede variar.
200: * @param value
201: */
202: public void setRowCount(int value) {
203: rowCount = value;
204: }
205:
206: /**
207: * Devuelve la cantidad de dimensiones que agrupan, se utiliza para conocer la cantidad de filas o columnas de la tabla
208: * dependiendo del modo
209: * @return
210: */
211: public int doGetColumnCount() {
212: int count = getDimensionCount();
213: return count;
214: }
215:
216: /**
217: * Metodo no implemntado de <code>AbstractTableModel</code>.
218: * @param rowIndex
219: * @param columnIndex
220: * @return
221: */
222: public Object getValueAt(int rowIndex, int columnIndex) {
223: return null;
224: }
225:
226: /**
227: * Devuelve el indice de una columna correspondiente a un nodo
228: * @param node
229: * @return
230: */
231: public int getNodeColumn(DimensionValueNode node) {
232: return node.getDimesionIndex();
233: }
234:
235: /**
236: * Devuelve un array de celdas que deben dibujarse para representar un nodo (si el nodo tiene subnodos, requerirá
237: * más de una celda para su representación)
238: * @param node
239: * @return
240: */
241: public int[][] getNodeCells(DimensionValueNode node, int row,
242: int size) {
243: if (node.getCollapsed()) {
244: return getCollapsedNodeCells(node, row);
245: } else {
246: return getExpandedNodeCells(node, row, size);
247: }
248: }
249:
250: /**
251: * Devuelve un array de celdas que deben dibujarse para representar un nodo colapsado
252: * @param node
253: * @param row
254: * @return
255: */
256: private int[][] getCollapsedNodeCells(DimensionValueNode node,
257: int row) {
258: int xSize = getDimensionCount() - (node.getDimesionIndex());
259: int[][] cells = new int[xSize][2];
260: for (int i = 0; i < xSize; i++) {
261: cells[i] = newCell(row, node.getDimesionIndex() + i);
262: }
263: return cells;
264: }
265:
266: /**
267: * Devuelve un array de celdas que deben dibujarse para representar un nodo expandido
268: * @param node
269: * @param row
270: * @param size
271: * @return
272: */
273: private int[][] getExpandedNodeCells(DimensionValueNode node,
274: int row, int size) {
275: int xSize = size;
276: int nodeDimIndex = node.getDimesionIndex();
277: int[][] cells = new int[xSize][2];
278: for (int i = 0; i < xSize; i++) {
279: cells[i] = newCell(row + i, nodeDimIndex);
280: }
281: return cells;
282: }
283:
284: /**
285: * Devuelve un array de celdas que deben dibujarse para representar un nodo como total
286: * @param node
287: * @param lastRow
288: * @return
289: */
290: public int[][] getTotalNodeCells(DimensionValueNode node,
291: int lastRow) {
292: int size = getDimensionCount() - (node.getDimesionIndex() + 1);
293: int[][] cells = new int[size][2];
294: for (int i = 0; i < size; i++) {
295: cells[i] = newCell(lastRow, node.getDimesionIndex() + 1 + i);
296: }
297: return cells;
298: }
299:
300: /**
301: * Crea una array de dos posiciones donde la primera es el indice de fila y la segunda es el indice de columna
302: * Dependiendo del modo, se decide a que posición del array se asigna cada parámetro (el resto de la clase trabaja
303: * como si siempre el modo fuera MODE_ROW y con este método se invierten las coordenadas en el caso de modo MODE_COLUMN
304: * @param row
305: * @param col
306: * @return
307: */
308: public int[] newCell(int row, int col) {
309: int[] cell = new int[2];
310: switch (mode) {
311: case MODE_ROW:
312: cell[0] = row;
313: cell[1] = col;
314: break;
315: case MODE_COLUMN:
316: cell[0] = col;
317: cell[1] = row;
318: break;
319: }
320: return cell;
321: }
322:
323: /**
324: * Devuelve el modo en que se dibuja la tabla
325: * @return
326: */
327: public int getMode() {
328: return mode;
329: }
330:
331: /**
332: * Asigna el modo en que se dibuja la tabla
333: * @param mode
334: */
335: public void setMode(int mode) {
336: this .mode = mode;
337: }
338:
339: /**
340: * Devuelve verdadero si el modo es colapsable (esto es si es un nodo que agrupa subnodos)
341: * @param node
342: * @return
343: */
344: public boolean getNodeIsCollapsable(DimensionValueNode node) {
345: return node.getDimesionIndex() < getDimensionValueNode()
346: .getDimensionCount() - 1;
347: }
348:
349: /**
350: * @param row
351: * @param col
352: * @return Devuelve true si el nodo es colapsable y se logró colapsar o expandir
353: */
354:
355: public boolean changeNodeState(int row, int col) {
356: DimensionValueNode root = getDimensionValueNode();
357: int internalRow;
358: int internalCol;
359: if (mode == HeaderTableModel.MODE_ROW) {
360: internalRow = row;
361: internalCol = col;
362: } else {
363: internalRow = col;
364: internalCol = row;
365: }
366: DimensionValueNode node = findCollapsableNode(root,
367: internalRow, internalCol, new RowIndexCarrier());
368: if (node != null) {
369: node.changeDimensionValueNodeState();
370: }
371: return node != null;
372: }
373:
374: /**
375: * Clase interna que se utiliza para acumular el valor del ultimo indice recorrido en metodos recursivos
376: */
377: private class RowIndexCarrier {
378: private int index = 0;
379:
380: public int getIndex() {
381: return index;
382: }
383:
384: public void inc(int value) {
385: index += value;
386: }
387: }
388:
389: /**
390: * Devuelve el nodo colapsable correspondiente a las coordenadas de fila y columna que se reciben como parámetro
391: * @param node
392: * @param row
393: * @param col
394: * @param carrier
395: * @return
396: */
397: private DimensionValueNode findCollapsableNode(
398: DimensionValueNode node, int row, int col,
399: RowIndexCarrier carrier) {
400: DimensionValueNode resultNode = null;
401: Iterator iterator = (node.getSubNodesList()).iterator();
402: if (carrier.getIndex() == row && node.getDimesionIndex() == col) {
403: resultNode = node;
404: } else {
405: if (!node.getCollapsed()) {
406: while (resultNode == null && iterator.hasNext()) {
407: DimensionValueNode subNode = (DimensionValueNode) iterator
408: .next();
409: if (carrier.getIndex() <= row
410: && subNode.getDimesionIndex() < getDimensionCount() - 1) {
411: resultNode = findCollapsableNode(subNode, row,
412: col, carrier);
413: }
414: if (subNode.getDimesionIndex() == getDimensionCount() - 1) {
415: carrier.inc(getCarrierIncrement());
416: }
417: }
418: }
419: if (node.getCollapsed() || getWithTotals()) {
420: carrier.inc(getCarrierIncrement());
421: }
422: }
423: return resultNode;
424: }
425:
426: private int getCarrierIncrement() {
427: if (getMode() == MODE_ROW) {
428: return 1;
429: } else {
430: return getVisibleMetricsCount();
431: }
432: }
433:
434: /**
435: * Devuelve los valores de las dimensiones para un indice de fila (o columna dependiendo del modo)
436: * @param index
437: * @return
438: */
439: public Object[] getValueFrom(int index) {
440: if (mode == MODE_ROW || getVisibleMetricsCount() == 1) {
441: DimensionValueNode node = (DimensionValueNode) getDimensionValueNode()
442: .getAllSubNodes().get(new Integer(index));
443: return getNodeValues(node);
444: } else {
445: int metricIndex = index % getVisibleMetricsCount();
446: int actualIndex = index - metricIndex;
447: DimensionValueNode node = (DimensionValueNode) getDimensionValueNode()
448: .getAllSubNodes().get(new Integer(actualIndex));
449: if (node == null && actualIndex == 0) {
450: node = getDimensionValueNode();
451: }
452: return getNodeValues(node, metricIndex);
453: }
454: }
455:
456: public Object[] getValuesFrom(int index) {
457: DimensionValueNode root = getDimensionValueNode();
458: return getValuesFrom(root, index, new IntegerExt(0));
459: }
460:
461: public Object[] getValuesFrom(DimensionValueNode node,
462: int wantedIndex, IntegerExt integer) {
463: boolean found = false;
464: Object[] returnVal = null;
465: if (node.getSubNodesList().size() == 0) {
466: if (integer.getCurrentValue() == wantedIndex) {
467: returnVal = getValuesFrom(node);
468: return returnVal;
469: } else {
470: integer.sumValue(1);
471: }
472: } else {
473: Collection nodes = node.getSubNodesList();
474: Iterator iterator = nodes.iterator();
475: while (iterator.hasNext() && !found) {
476: DimensionValueNode currentNode = (DimensionValueNode) iterator
477: .next();
478: returnVal = getValuesFrom(currentNode, wantedIndex,
479: integer);
480: if (returnVal != null) {
481: found = true;
482: }
483: }
484: }
485: return returnVal;
486: }
487:
488: private Object[] getValuesFrom(DimensionValueNode leaf) {
489: Object returnVal[];
490: if (mode == MODE_ROW) {
491: returnVal = new Object[getColumnCount()];
492: fillArray(returnVal, leaf, returnVal.length - 1, "ROWS");
493: } else {
494: returnVal = new Object[getRowCount()];
495: fillArray(returnVal, leaf, returnVal.length - 1, "COLUMNS");
496: }
497: return returnVal;
498: }
499:
500: private void fillArray(Object[] returnVal, DimensionValueNode node,
501: int i, String mode) {
502: if (!node.getValue().toString().equalsIgnoreCase(mode)) {
503: returnVal[i] = node.getValue().toString();
504: fillArray(returnVal, node.getParentNode(), i - 1, mode);
505: }
506: }
507:
508: /**
509: * Devuelve el título de una métrica segun el indice en que aparecen el la ReportQuery
510: * @param index
511: * @return
512: */
513: public String getMetricCaption(int index) {
514: QueryMetric metric = (QueryMetric) reportQuery
515: .getVisibleMetrics().get(index);
516: return reportSpec.getMetricFromName(metric.getName())
517: .getCaption();
518: }
519:
520: /**
521: * Devuelve los valores de las dimensiones para un indice de fila (o columna dependiendo del modo), para el caso en que
522: * hay mas de una métrica, se debe especificar el nombre de la métrica además de los valores de dimensiones
523: * @param node
524: * @param metricIndex
525: * @return
526: */
527: private Object[] getNodeValues(DimensionValueNode node,
528: int metricIndex) {
529: Object[] auxValues = getNodeValues(node);
530: Object[] result = new Object[auxValues.length + 1];
531: System.arraycopy(auxValues, 0, result, 0, auxValues.length);
532: result[result.length - 1] = getMetricCaption(metricIndex);
533: return result;
534: }
535:
536: /**
537: * Devuelve los valores de las dimensiones para un indice de fila (o columna dependiendo del modo)
538: * @param node
539: * @return
540: */
541: private Object[] getNodeValues(DimensionValueNode node) {
542: Object[] values = new Object[getDimensionCount()];
543: DimensionValueNode currentNode = node;
544: while ((currentNode != null)
545: && currentNode.getDimesionIndex() >= 0) {
546: values[currentNode.getDimesionIndex()] = currentNode
547: .getValue().toString();
548: currentNode = currentNode.getParentNode();
549: }
550: return values;
551: }
552:
553: /**
554: * Obtiene el FooterCaption para la dimension.
555: * @param dimesionIndex
556: * @return
557: */
558: public String getGroupFooterCaption(int dimesionIndex) {
559: if (dimesionIndex >= 0) {
560: List dimensions;
561: if (getMode() == MODE_COLUMN) {
562: dimensions = reportQuery.getColumnDimensions();
563: } else {
564: dimensions = reportQuery.getRowDimensions();
565: }
566: QueryDimension dimension = (QueryDimension) dimensions
567: .get(dimesionIndex);
568: String result = reportSpec.getDimensionFromName(
569: dimension.getName()).getGroupFooterCaption();
570: if ((result != null) && (result != ""))
571: return result;
572: }
573: return LanguageTraslator.traslate("358");
574: }
575:
576: /**
577: * Calcula si una row es total
578: * @param row
579: * @return
580: */
581: public boolean isTotalRow(int row) {
582: if (!reportQuery.isVisibleTotals()) {
583: return false;
584: }
585: Vector totals = new Vector();
586: fillTotalRows(totals, this .getDimensionValueNode());
587: iterations = 0;
588: return totals.contains(new Integer(row));
589: }
590:
591: /**
592: * Calcula si una columna es total
593: * @param col
594: * @return
595: */
596: public boolean isTotalCol(int col) {
597: if (!reportQuery.isVisibleTotals()) {
598: return false;
599: }
600: Vector totals = new Vector();
601: fillTotalCols(totals, this .getDimensionValueNode());
602: iterations = 0;
603: return totals.contains(new Integer(col));
604: }
605:
606: /**
607: * Llena un Vector con las columnas que son totales
608: * @param totals
609: * @param node
610: */
611: public void fillTotalCols(Vector totals, DimensionValueNode node) {
612: if (node.getSubNodesList().isEmpty()) {
613: iterations += getVisibleMetricsCount();
614: return;
615: }
616: Iterator iterator = node.getSubNodesList().iterator();
617: while (iterator.hasNext()) {
618: DimensionValueNode current = (DimensionValueNode) iterator
619: .next();
620: fillTotalCols(totals, current);
621: }
622: for (int i = 0; i < getVisibleMetricsCount(); i++) {
623: totals.add(new Integer(iterations));
624: iterations++;
625: }
626: }
627:
628: /**
629: * Calcula las filas totales en base al nodo ROW y llena un vector con los resultados
630: * @param totals
631: * @param node
632: */
633: public void fillTotalRows(Vector totals, DimensionValueNode node) {
634: if (node.getSubNodesList().isEmpty()) {
635: iterations++;
636: return;
637: }
638: Iterator iterator = node.getSubNodesList().iterator();
639: while (iterator.hasNext()) {
640: DimensionValueNode current = (DimensionValueNode) iterator
641: .next();
642: fillTotalRows(totals, current);
643: }
644: totals.add(new Integer(iterations));
645: iterations++;
646: }
647:
648: }
|