KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > calipso > reportgenerator > reportcalculator > Cube


1 package com.calipso.reportgenerator.reportcalculator;
2
3 import com.calipso.reportgenerator.common.InfoException;
4
5 import java.util.*;
6 import java.io.Serializable JavaDoc;
7 import java.io.ObjectOutputStream JavaDoc;
8 import java.io.IOException JavaDoc;
9 import java.io.ObjectInputStream JavaDoc;
10
11 import com.calipso.reportgenerator.common.ReportMetricSpec;
12 import com.calipso.reportgenerator.common.LanguageTraslator;
13 import com.calipso.reportgenerator.reportdefinitions.types.CalculationType;
14
15 /**
16  * La clase Cube representa la información de un reporte.
17  * Podríamos verlo como el resultado de la aplicación de una
18  * query a un Pivot. La estructura del Cube es un árbol.
19  * En la raíz del mismo hay un array que contiene una serie de
20  * dimensiones seguida de las métricas.
21  * Mientras en las métricas está acumulado el total de cada una, en las
22  * dimensiones y diccionarios. Cada uno de estos diccionarios contiene, como
23  * clave, los distintos valores para la dimension correspondiente (la posición
24  * en el array). Como valor, los diccionarios contienen un nuevo array similar
25  * al de la raíz.
26  * El resultado es una estructura arbórea que alterna nodos tipo array con
27  * diccionarios.
28  * Por cuestiones de performance se ha utilizado la clase SharedFloat en lugar
29  * de los Float tradicionales
30  * @see com.calipso.reportgenerator.reportcalculator.CubeQuery
31  * @see Pivot
32  * @see SharedFloat
33  */

34
35 public class Cube implements Serializable JavaDoc, PivotClient {
36   private CubeDefinition definition;
37   private CubeQuery query;
38   private Object JavaDoc[] root;
39   private int arraySize;
40   private int metricsSize;
41   private LinkedList dimensionsCombinations;
42   private Set[] dimensionValues;
43   private MetricCalculationStrategy[] metricStrategies;
44   private MetricCalculationStrategy[] groupFooterStrategies;
45   private int dimensionsSize;
46   private int[] queryMetrics;
47   private static final int INVALID_DIMENSION = 999;
48   private Object JavaDoc[] metrics;
49   private Map excludedNodes = new HashMap();
50   private static final String JavaDoc others = LanguageTraslator.traslate("404").intern();//SharedString.newFrom(LanguageTraslator.traslate("404"));
51
private Pivot pivot;
52   private int fullDimensionsSize;
53
54   /**
55    * Retorna un entero que determina la cantidad de dimensiones
56    * @return
57    */

58   public int getDimensionsSize() {
59     return dimensionsSize;
60   }
61
62   /**
63    * Inicializa la cantidad de dimensiones y métricas del Cube
64    * a partir de la cantidad de dimensiones y métricas de la definicion.
65    * Inicializa la variable arraySize de acuerdo a la suma de metricas
66    * y dimensiones, es decir, el total de columnas.
67    * Tambien inicializa las formas de calculo de metricas y totales. Inicializa las
68    * metricas auxiliares para el cálculo de promedios.
69    */

70   private void initialize() {
71     metrics = definition.getMetrics();
72     initAverageCountMetrics();
73     metricsSize = getMetrics().length;
74     dimensionsSize = query.getDimensions().length;//definition.getDimensions().length;
75
fullDimensionsSize = definition.getDimensions().length;
76     setArraySize(dimensionsSize + metricsSize);
77     initMetricStrategies();
78     initGroupFooterStrategies();
79     excludedNodes = new HashMap();
80   }
81
82   /**
83    * Inicializa las metricas adicionales que se usaran para el calculo de los promedios. Por cada metrica crea
84    * una adicional que contara las ocurrencias.
85    */

86   private void initAverageCountMetrics() {
87     if(this.hasAverageMetrics()){
88       ReportMetricSpec countMetric;
89       Object JavaDoc[] resultMetrics = new Object JavaDoc[(metrics.length * 2)];
90       for (int i = 0; i < metrics.length; i++) {
91         resultMetrics[i] = metrics[i];
92         countMetric = new ReportMetricSpec("Count" + i);
93         countMetric.setAggregateType(CalculationType.COUNT);
94         countMetric.setGroupFooterType(CalculationType.COUNT);
95         resultMetrics[metrics.length + i] = countMetric;
96       }
97       metrics = resultMetrics;
98     }
99   }
100
101   /**
102    * Retorna verdadero si existen metricas que utilizan average.
103    * @return
104    */

105   private boolean hasAverageMetrics() {
106     return getAverageMetricsIndexes().length > 0;
107   }
108
109   /**
110    * Busca las metricas que utilizan average como tipo de calculo. Arma un array con los indices de dichas metricas.
111    * @return un array con los indices de las metricas que utilizan average
112    */

113   private int[] getAverageMetricsIndexes() {
114     Object JavaDoc[] metrics = definition.getMetrics();
115     LinkedList indexes = new LinkedList();
116     for (int i = 0; i < metrics.length; i++) {
117       Object JavaDoc metric = metrics[i];
118       if(metric instanceof ReportMetricSpec){
119         ReportMetricSpec metricSpec = (ReportMetricSpec)metric;
120         if((metricSpec.getAggregateType().getType() == CalculationType.AVERAGE_TYPE ||
121            metricSpec.getGroupFooterType().getType() == CalculationType.AVERAGE_TYPE) &&
122            isMetricInQuery(i)){
123           indexes.add(new Integer JavaDoc(i));
124         }
125       }
126     }
127     return toIntArray(indexes);
128   }
129
130   /**
131    * Retorna true si la metrica pasada por parametro esta en la query
132    * @param i indice de la metrica que se busca en la query
133    * @return true si la metrica esta en la query
134    */

135   private boolean isMetricInQuery(int i) {
136     for (int j = 0; j < queryMetrics.length; j++) {
137       if(i + definition.getDimensions().length == queryMetrics[j]){
138         return true;
139       }
140     }
141     return false;
142   }
143
144   /**
145    * Inicializa el array con las estrategias de calculo de los totales tomadas de los ReportMetricSpec de cada metrica.
146    */

147   private void initGroupFooterStrategies() {
148     Object JavaDoc[] metrics = getMetrics();
149     groupFooterStrategies = new MetricCalculationStrategy[metrics.length];
150     for (int i = 0; i < metrics.length; i++) {
151       Object JavaDoc metric = metrics[i];
152       if(metric instanceof ReportMetricSpec){
153         groupFooterStrategies[i] = MetricCalculationStrategy.getFooterStrategyFor((ReportMetricSpec)metric);
154       }else{
155         groupFooterStrategies[i] = new SumStrategy();
156       }
157     }
158   }
159
160   /**
161    * Retorna las metricas del cubo. Normalmente seran las metricas que esten en el definition, pero si
162    * existen metricas que calculen AVERAGE las metricas estaran duplicadas (dicha replica contara
163    * realizara un COUNT para porder tener la cantidad sobre la cual dividir).
164    * @return
165    */

166   private Object JavaDoc[] getMetrics() {
167     return metrics;
168   }
169
170   /**
171    * Inicializa las estrategias de calculo para cada metrica.
172    */

173   private void initMetricStrategies() {
174     Object JavaDoc[] metrics = getMetrics();
175     metricStrategies = new MetricCalculationStrategy[metrics.length];
176     for (int i = 0; i < metrics.length; i++) {
177       Object JavaDoc metric = metrics[i];
178       if(metric instanceof ReportMetricSpec){
179         metricStrategies[i] = MetricCalculationStrategy.getMetricStrategyFor((ReportMetricSpec)metric);
180       }else{
181         metricStrategies[i] = new SumStrategy();
182       }
183     }
184   }
185
186   /**
187    * Retorna la definicion del Cube
188    * @return
189    */

190   public CubeDefinition getDefinition() {
191     return definition;
192   }
193
194   /**
195    * Asigna al Cube la definición de Cube correspondiente
196    * @param definition
197    */

198   public void setDefinition(CubeDefinition definition) {
199     this.definition = definition;
200   }
201
202   /**
203    * Retorna la Query del Cube
204    * @return
205    */

206   public CubeQuery getQuery() {
207     return query;
208   }
209
210   /**
211    * Asigna la nueva query a ejecutar
212    * @param query
213    * @throws InfoException Si se produjo un error en el cálculo del cubo.
214    */

215   public void setQuery(CubeQuery query) throws InfoException {
216     try {
217       this.query = query;
218       queryMetrics = getQuery().getMetrics();
219       setDimensionsCombinations();
220     }
221     catch(Exception JavaDoc e){
222       throw new InfoException(com.calipso.reportgenerator.common.LanguageTraslator.traslate("102"),e);
223     }
224   }
225
226   /**
227    * Retorna un array que representa la raíz del cube
228    * y que contiene una serie de dimensiones seguidas de las métricas
229    * @return
230    */

231   public Object JavaDoc[] getRoot() {
232     return root;
233   }
234
235   /**
236    * Asigna un array que representa la raíz del cube y que contiene
237    * una serie de dimensiones seguidas de las métricas
238    * @param root
239    */

240   public void setRoot(Object JavaDoc[] root) {
241     this.root = root;
242   }
243
244   /**
245    * Retorna arraySize que es un entero que
246    * representa la suma de dimensiones y métricas, es decir el total de columnas del Cube.
247    * @return
248    */

249   public int getArraySize() {
250     return arraySize;
251   }
252
253   /**
254    * Inicializa arraySize a partir de un entero que
255    * representa la suma de dimensiones y métricas, es decir el total de columnas del Cube.
256    * @param arraySize
257    */

258   public void setArraySize(int arraySize) {
259     this.arraySize = arraySize;
260   }
261
262   /**
263    * Retorna la cantidad total de métricas del Cube.
264    * @return
265    */

266   public int getMetricsSize() {
267     return metricsSize;
268   }
269
270   /**
271    * Crea todas las combinaciones de dimensiones necesarias para cualquier tipo de totales
272    * @throws InfoException
273    */

274  private void setDimensionsCombinations() throws InfoException {
275     dimensionsCombinations = new LinkedList();
276     int[] cols = query.getColumns();
277     int[] rows = query.getRows();
278     for(int i=1; i <= rows.length; i++){
279       int[] list = new int[i];
280       for(int j=0; j < i; j++){
281         list[j] = j;
282       }
283       dimensionsCombinations.add(list);
284       for(int z=1; z <= cols.length; z++){
285         int[] withCol = new int[i + z];
286         System.arraycopy(list, 0, withCol, 0, list.length);
287         for(int j=0; j < z; j++){
288           withCol[j + i] = rows.length + j;
289         }
290         dimensionsCombinations.add(withCol);
291       }
292     }
293     if(rows.length != 0){
294       for(int i=1; i <= cols.length; i++){
295         int[] list = new int[i];
296         for(int j=0; j < i; j++){
297           list[j] = rows.length + j;
298         }
299         dimensionsCombinations.add(list);
300       }
301     }
302     /*int[] dimensions;
303     int factor;
304     int[] list;
305     int queryDimensionsSize;
306     int combination;
307     String bits;
308     int bitsLenght;
309     int pos;
310     try{
311       dimensions = getIntegerArray(getQuery().getDimensions().length);
312       queryDimensionsSize = dimensions.length;
313       dimensionsCombinations = new LinkedList();
314       for (combination = 1; combination <= (Math.pow(2, queryDimensionsSize) - 1); combination++) {
315       bits = Integer.toBinaryString(combination);
316       bitsLenght = bits.length();
317       list = new int[bitsLenght];
318       for (int index = 0; index < bitsLenght; index++) {
319         pos = bitsLenght - 1 - index;
320         factor = Character.digit(bits.charAt(pos), 10);
321         if (factor == 1) {
322           list[index] = factor * dimensions[index];
323         }
324         else {
325           list[index] = INVALID_DIMENSION;
326         }
327       }
328         //if(list[0]==0){
329           dimensionsCombinations.add(list);
330         //}
331       }
332     }
333     catch (Exception e){
334       throw new InfoException(com.calipso.reportgenerator.common.LanguageTraslator.traslate("103"),e);
335     } */

336   }
337
338   public int[] getIntegerArray(int base, int length) {
339     int[] result = new int[length];
340     for (int j=0, i = base; j < result.length; i++, j++) {
341       result[j] = i;
342     }
343     return result;
344   }
345
346   /**
347    * Reinicializa el Cube. Como efecto se pierden todos los datos actuales.
348    */

349   public void reset() {
350     initialize();
351     root = null;
352     System.gc();
353     root = newArray();
354   }
355
356
357   /**
358    * Crea un array para un nodo con el tamaño correspondiente según la cantidad de dimensiones y métricas
359    * inicializando los valores de las métricas con 0
360    *
361    * @return
362    */

363   private Object JavaDoc[] newArray() {
364     Object JavaDoc[] result;
365     int index;
366     result = new Object JavaDoc[arraySize];
367     for (index = dimensionsSize; index < arraySize; index++) {
368       result[index] = SharedFloat.newFrom(Float.NaN);
369     }
370
371     return result;
372   }
373
374   /**
375    * Agrega row si pasa por los filtros
376    * @param row
377    */

378   public void fillWith(Object JavaDoc[] row) {
379     if (query.matches(row)) {
380       fillDimensionValues(row);
381       if (query.valuesEnabled(row)) {
382         row = shrinkRow(row);
383         basicFillWith(row);
384       }
385     }else if(query.isGroupExcludedValues()){
386       if(query.otherFilterMatches()){
387         row = shrinkRow(row);
388         fillExcludedValueNode(row);
389       }
390     }
391   }
392
393   /**
394    * Selecciona de row solo las dimensiones necesarias y las ordena
395    * @param row
396    * @return
397    */

398   private Object JavaDoc[] shrinkRow(Object JavaDoc[] row) {
399     int[] dimensions = query.getDimensions();
400     int[] metrics = query.getMetrics();
401     Object JavaDoc[] result = new Object JavaDoc[dimensions.length + metrics.length];
402     int i = 0;
403     for (; i < dimensions.length; i++) {
404       result[i] = row[dimensions[i]];
405     }
406     for (int j = 0; j < metrics.length; i++, j++) {
407       result[i] = row[metrics[j]];
408     }
409     return result;
410   }
411
412   private void fillExcludedValueNode(Object JavaDoc[] aRow) {
413     Object JavaDoc[] measures;
414     Iterator iterator;
415     //EnumerationCubeFilter filter = query.getExcludeGroupFilter();
416
measures = valuesOfFrom(queryMetrics, aRow);
417     iterator = dimensionsCombinations.iterator();
418     while (iterator.hasNext()) {
419       /* Agarro dimension por dimension. Me fijo si el valor de esa dimension en
420       este row esta o va a otros.
421       Si esta, busco su nodo y la agrego. Pero en los subnodos me tengo que fijar
422       si los valores de esas dimensiones estan o van en otros.
423       Si va a otros, agrego al nodo de otros de esta dimension, pero nuevamente
424       en los subnodos debo ver si esta el valor o va a otros.
425       */

426       excludedAdd((int[]) iterator.next(), aRow, measures);
427     }
428     addTotal(measures, root, aRow);
429   }
430
431   private void excludedAdd(int[] dimensions, Object JavaDoc[] aRow, Object JavaDoc[] measures) {
432     Object JavaDoc[] node;
433     node = getExcludedNode(dimensions, aRow);
434     if(isLastLevel(dimensions)){
435       addTo(measures, node, aRow);
436     }else{
437       addTotal(measures, node, aRow);
438     }
439   }
440
441   private Object JavaDoc[] getExcludedNode(int[] dimensions, Object JavaDoc[] aRow) {
442     Object JavaDoc[] node = root;
443     for (int i = 0; i < dimensions.length; i++) {
444       int dimension = dimensions[i];
445       if (dimension != INVALID_DIMENSION) {
446         if(node[dimension]==null){
447           node[dimension] = new HashMap();
448         }
449         Object JavaDoc value;
450         //Verifica si el filtro que excluye valores tiene la dimension, y luego que el valor este excluido (no este en el filtro).
451
if(query.getExcludeGroupFilter().hasDimension(dimension) && !query.getExcludeGroupFilter().containsValue(dimension, aRow[dimension])){
452           value = others;
453         }else{
454           value = aRow[dimension];
455         }
456         if(!((HashMap)node[dimension]).containsKey(value)){
457           ((HashMap)node[dimension]).put(value, newArray());
458         }
459         node = (Object JavaDoc[])((HashMap)node[dimension]).get(value);
460       }
461     }
462     /*Iterator it = excludedNodes.entrySet().iterator();
463     Object[] excludedNode = null;
464     while(it.hasNext() && excludedNode==null){
465       Map.Entry entry = (Map.Entry)it.next();
466       if(Arrays.equals((int [])entry.getKey(), dimensions)){
467         excludedNode = (Object[])entry.getValue();
468       }
469     }
470     if(excludedNode==null){
471       excludedNode = newExcludedNode(dimensions);
472       excludedNodes.put(dimensions, excludedNode);
473       //addExcludedNode(excludedNode, dimensions);
474     }
475     return excludedNode;*/

476     return node;
477   }
478
479   private void addExcludedNode(Object JavaDoc[] excludedNode, int[] dimensions) {
480     Object JavaDoc[] node = root;
481     for (int i = 0; i < dimensions.length - 1; i++) {
482       int dimension = dimensions[i];
483       if(dimension != INVALID_DIMENSION){
484         HashMap map = (HashMap)node[dimension];
485         if(map.containsKey(others)){
486           node = (Object JavaDoc[])map.get(others);
487         }
488       }
489     }
490     Object JavaDoc child = node[dimensions[dimensions.length - 1]];
491     if(child==null){
492       child = new HashMap();
493       node[dimensions[dimensions.length - 1]] = child;
494     }
495     ((Map)child).put(others, excludedNode);
496   }
497
498   private Object JavaDoc[] newExcludedNode(int[] dimensions) {
499     Object JavaDoc[] node = newArray();
500     for (int i = 0; i < dimensions.length; i++) {
501       int dimension = dimensions[i];
502       if(dimension != INVALID_DIMENSION){
503         HashMap dict = new HashMap();
504         Object JavaDoc[] subNode = getSubNode(dimensions, i+1);
505         dict.put(others, subNode);
506         node[dimension] = dict;
507       }
508     }
509     return node;
510   }
511
512   private Object JavaDoc[] getSubNode(int[] dimensions, int index) {
513     Object JavaDoc[] node = newArray();
514     for (int j = index; j < dimensions.length; j++) {
515       int dimension = dimensions[j];
516       if(dimension != INVALID_DIMENSION){
517         HashMap dict = new HashMap();
518         Object JavaDoc[] subNode = getSubNode(dimensions, j+1);
519         dict.put(others, subNode);
520         node[dimension] = dict;
521       }
522     }
523     return node;
524   }
525
526   /**
527    * Resuelve la inclusión de un nuevo row actualizando los totales correspondientes
528    * @param aRow
529    */

530   protected void basicFillWith(Object JavaDoc[] aRow) {
531     Object JavaDoc[] measures;
532     Iterator iterator;
533     measures = valuesOfFrom(queryMetrics, aRow);
534     iterator = dimensionsCombinations.iterator();
535     while (iterator.hasNext()) {
536       atFromAdd((int[]) iterator.next(), aRow, measures);
537     }
538     addTotal(measures, root, aRow);
539   }
540
541   private void fillDimensionValues(Object JavaDoc[] row) {
542     int[] dims;
543     dims = query.getRows();
544     fillDimensionValuesFromArray(dims, row);
545     dims = query.getColumns();
546     fillDimensionValuesFromArray(dims, row);
547     dims = query.getPages();
548     fillDimensionValuesFromArray(dims, row);
549   }
550
551   private void fillDimensionValuesFromArray(int[] dimensions, Object JavaDoc[] row) {
552     for (int i = 0; i < dimensions.length; i++) {
553       int dim = dimensions[i];
554       if (row[dim] != null){
555         getDimensionValues()[dim].add(row[dim]);
556       }
557     }
558   }
559
560
561   /**
562    * Resuelve la operación de sumarización
563    * @param measures
564    * @param node
565    */

566   private void addTo(Object JavaDoc[] measures, Object JavaDoc[] node, Object JavaDoc[] aRow) {
567     int index;
568     for (index = 0; index < metricsSize; index++) {
569       node[index + dimensionsSize] = (metricStrategies[index]).operate(node, index + dimensionsSize, measures[index], aRow);
570               //((SharedFloat) node[index + dimensionsSize]).add(measures[index]);
571
}
572   }
573
574   /**
575    * Suma los measures en las coordenadas dadas por dimensions y el contenido de aRow
576    * @param dimensions
577    * @param aRow
578    * @param measures
579    */

580   private void atFromAdd(int[] dimensions, Object JavaDoc[] aRow, Object JavaDoc[] measures) {
581     Object JavaDoc[] node;
582     node = atFrom(dimensions, aRow);
583     if (node != null) {
584       if(isLastLevel(dimensions)){
585         addTo(measures, node, aRow);
586       }else{
587         addTotal(measures, node, aRow);
588       }
589     }
590   }
591
592   /**
593    * Agrega valores totales al cubo, realizando el calculo segun la estrategia de total que tenga la metrica.
594    * @param measures los valores a agregar
595    * @param node el nodo donde se opera
596    */

597   private void addTotal(Object JavaDoc[] measures, Object JavaDoc[] node, Object JavaDoc[] aRow) {
598     for (int index = 0; index < metricsSize; index++) {
599       node[index + dimensionsSize] = (groupFooterStrategies[index]).operate(node, index + dimensionsSize, measures[index], aRow);
600     }
601   }
602
603   /**
604    * Comprueba si las dimensiones especificadas darian un nodo final (de ultimo nivel) o si es un nodo que agrupa.
605    * @param dimensions
606    * @return
607    */

608   private boolean isLastLevel(int[] dimensions) {
609     if(dimensions.length < getQuery().getDimensions().length){
610       return false;
611     }
612     for (int i = 0; i < dimensions.length; i++) {
613       int dimension = dimensions[i];
614       if(dimension == INVALID_DIMENSION){
615         return false;
616       }
617     }
618     return true;
619   }
620
621   /**
622    * Retorna los valores para las coordenadas dadas por dimensions y el contenido de aRow
623    * Va recorriendo recursivamente los nodos del tipo array hasta agotar las dimensions
624    * @param dimensions
625    * @param aRow
626    * @return
627    */

628   private Object JavaDoc[] atFrom(int[] dimensions, Object JavaDoc[] aRow) {
629     Object JavaDoc[] node;
630     int index;
631     int dimension;
632     int dimensionsLenght;
633     Object JavaDoc value;
634
635     node = root;
636     dimensionsLenght = dimensions.length;
637     for (index = 0; index < dimensionsLenght; index++) {
638       dimension = dimensions[index];
639       if (dimension != INVALID_DIMENSION) {
640         value = aRow[dimension];
641         if (value != null) {
642           node = atDimensionIn(value, dimension, node);
643         }
644         else {
645           return null;
646         }
647       }
648     }
649     return node;
650   }
651
652   /**
653    * Retorna en un array los valores contenidos en aRow para las métricas dadas por metrics
654    * @param metrics
655    * @param aRow
656    * @return
657    */

658   private Object JavaDoc[] valuesOfFrom(int[] metrics, Object JavaDoc[] aRow) {
659     Object JavaDoc[] array = new Object JavaDoc[metricsSize];
660     System.arraycopy(aRow, query.getDimensions().length, array, 0, query.getMetrics().length);
661     /*array = new Object[metricsSize];
662 for (int index = 0; index < metrics.length; index++) {
663 if (aRow[metrics[index]]!=null){
664   array[index] = (/*(SharedFloat)*/
/*aRow[metrics[index]]);
665       }else{
666         array[index] = null;
667       }
668     }*/

669     return array;
670     /* float[] array;
671
672       array = new float[metricsSize];
673         for ( int index = 0; index < metricsSize; index++ )
674         {
675             array[index] = ((Float) aRow[ metrics[index] ]).floatValue();
676         }
677
678         return array;*/

679   }
680
681   /**
682    * Retorna el porcentaje, con respecto a la fila, de la métrica metric según las
683    * coordenadas indicadas por dimensions y values
684    * @param metric
685    * @param dimensions
686    * @param values
687    * @return
688    */

689   public float rowPercentageOf(int metric, int[] dimensions, Object JavaDoc[] values) {
690     float total;
691
692     total = rowTotalOf(metric, dimensions, values);
693     return percentageOf(metric, dimensions, values, total);
694   }
695
696   /**
697    * Devuelve el total de una métrica en una fila para las dimensiones que se reciben como parámetro
698    * @param metric
699    * @param dimensions
700    * @param values
701    * @return
702    */

703   private float rowTotalOf(int metric, int[] dimensions, Object JavaDoc[] values) {
704     return totalOf(metric, dimensions, values, getQuery().getRows());
705   }
706
707   /**
708    * Retorna el porcentaje, con respecto a la columna, de la métrica metric según las
709    * coordenadas indicadas por dimensions y values
710    * @param metric
711    * @param dimensions
712    * @param values
713    * @return
714    */

715   public float columnPercentageOf(int metric, int[] dimensions, Object JavaDoc[] values) {
716     float total;
717
718     total = columnTotalOf(metric, dimensions, values);
719     return percentageOf(metric, dimensions, values, total);
720   }
721
722
723   /**
724    * Devuelve el total de una columna para una métrica para las dimensiones que se reciben como parámetro
725    * @param metric
726    * @param dimensions
727    * @param values
728    * @return
729    */

730   private float columnTotalOf(int metric, int[] dimensions, Object JavaDoc[] values) {
731     return totalOf(metric, dimensions, values, getQuery().getColumns());
732   }
733
734
735   /**
736    * Devuelve el total de una métrica para las dimensiones que se reciben como parámetro
737    * @param metric
738    * @param dimensions
739    * @param values
740    * @param rows
741    * @return
742    */

743   private float totalOf(int metric, int[] dimensions, Object JavaDoc[] values, int[] rows) {
744     int dimension;
745     LinkedList selectedDimensions;
746     LinkedList selectedValues;
747     boolean found;
748     int row;
749     int dimensionsLenght;
750     int rowsLenght;
751
752     selectedDimensions = new LinkedList();
753     selectedValues = new LinkedList();
754     dimensionsLenght = dimensions.length;
755     for (int index = 0; index < dimensionsLenght; index++) {
756       dimension = dimensions[index];
757       rowsLenght = rows.length;
758       found = false;
759       for (row = 0; (row < rowsLenght) && !found; row++) {
760         found = (rows[row] == dimension);
761       }
762       if (found) {
763         selectedDimensions.add(new Integer JavaDoc(dimension));
764         selectedValues.add(values[index]);
765       }
766     }
767
768     return measureAtDimensionsValues(metric, toIntArray(selectedDimensions), selectedValues.toArray());
769   }
770
771   /**
772    * Devuelve un array de enteros con los índices seleccionados
773    * @param selectedIndexes
774    * @return
775    */

776   private int[] toIntArray(LinkedList selectedIndexes) {
777     int[] result;
778     int index;
779     Iterator iterator;
780     int selectedDimensionsSize;
781
782     result = new int[selectedIndexes.size()];
783     iterator = selectedIndexes.iterator();
784     selectedDimensionsSize = selectedIndexes.size();
785     for (index = 0; index < selectedDimensionsSize; index++) {
786       result[index] = ((Integer JavaDoc) iterator.next()).intValue();
787     }
788     return result;
789   }
790
791   /**
792    * Devuelve el porcentaje de una métrica para las dimensiones especificadas
793    * @param metric
794    * @param dimensions
795    * @param values
796    * @param total
797    * @return
798    */

799   private float percentageOf(int metric, int[] dimensions, Object JavaDoc[] values, float total) {
800     float value;
801
802     value = measureAtDimensionsValues(metric, dimensions, values);
803     if (total == 0) {
804       return 0;
805     }
806     else {
807       return value * 100 / total;
808     }
809   }
810
811   /**
812    * Retorna el valor de la métrica metric según las coordenadas indicadas por
813    * dimensions y values
814    * @param metric
815    * @param dimensions
816    * @param values
817    * @return
818    */

819   public float measureAtDimensionsValues(int metric, int[] dimensions, Object JavaDoc[] values) {
820     int metricIndex;
821     Object JavaDoc[] measures;
822
823     for (metricIndex = 0; metricIndex < getMetricsSize(); metricIndex++) {
824       if (getQuery().getMetrics()[metricIndex] == metric) {
825         break;
826       }
827     }
828     measures = measuresAtDimensionsValues(dimensions, values);
829
830     return ((SharedFloat) measures[metricIndex + dimensionsSize]).floatValue();
831   }
832
833   /**
834    * Retorna los valores de las métricas según las coordenadas indicadas por
835    * dimensions y values
836    * @param dimensions
837    * @param values
838    * @return
839    */

840   public Object JavaDoc[] measuresAtDimensionsValues(int[] dimensions, Object JavaDoc[] values) {
841     Object JavaDoc[] node;
842     int index;
843     int dimensionsLenght;
844
845     node = root;
846     dimensionsLenght = dimensions.length;
847     for (index = 0; index < dimensionsLenght; index++) {
848       node = atDimensionIn(values[index], dimensions[index], node);
849     }
850     return node;
851   }
852
853   /**
854    * Retorna el nodo tipo array a partir del node y el valor value para la
855    * dimensión. Si no existen el nodo tipo diccionario y el tipo
856    * array, los crea
857    * @param value
858    * @param dimension
859    * @param node
860    * @return
861    */

862   private Object JavaDoc[] atDimensionIn(Object JavaDoc value, int dimension, Object JavaDoc[] node) {
863     HashMap dict;
864     Object JavaDoc[] array;
865     Object JavaDoc o;
866
867     if ((node[dimension] instanceof HashMap)) {
868       dict = (HashMap) node[dimension];
869     }
870     else {
871       node[dimension] = dict = new HashMap();
872     }
873     o = dict.get(value);
874     if (o == null) {
875       array = newArray();
876       dict.put(value, array);
877       return array;
878     }
879     else {
880       return (Object JavaDoc[]) o;
881     }
882   }
883
884   /**
885    * Resolución de la serialización
886    * @param stream
887    * @throws IOException
888    */

889   public void writeTo(ObjectOutputStream JavaDoc stream) throws IOException JavaDoc {
890     stream.writeObject(this);
891   }
892
893   /**
894    * Resolución de la des-serialización
895    * @param stream
896    * @throws IOException
897    * @throws ClassNotFoundException
898    */

899   public void readFrom(ObjectInputStream JavaDoc stream, Pivot pivot) throws IOException JavaDoc, ClassNotFoundException JavaDoc {
900     Cube cube;
901
902     pivot = null;
903     cube = (Cube) stream.readObject();
904     this.arraySize = cube.arraySize;
905     this.definition = cube.definition;
906     this.dimensionsCombinations = cube.dimensionsCombinations;
907     this.dimensionsSize = cube.dimensionsSize;
908     this.metricsSize = cube.metricsSize;
909     this.query = cube.query;
910     this.queryMetrics = cube.queryMetrics;
911     this.root = cube.root;
912   }
913
914   /**
915    * Incompleto. Es para agregar incrementalmente una dimensión
916    * @param dimension
917    */

918   public void addDimension(int dimension) {
919     int[] dimensions;
920     int factor;
921     int[] list;
922     int queryDimensionsSize;
923     int combination;
924     String JavaDoc bits;
925     int bitsLenght;
926     int pos;
927
928     dimensions = getQuery().getDimensions();
929     queryDimensionsSize = dimensions.length;
930     dimensionsCombinations = new LinkedList();
931     for (combination = 1; combination <= (Math.pow(2, queryDimensionsSize) - 1); combination++) {
932       bits = Integer.toBinaryString(combination);
933       bitsLenght = bits.length();
934       list = new int[bitsLenght];
935       for (int index = 0; index < bitsLenght; index++) {
936         pos = bitsLenght - 1 - index;
937         factor = Character.digit(bits.charAt(pos), 10);
938         if (factor == 1) {
939           list[index] = factor * dimensions[index];
940         }
941         else {
942           list[index] = INVALID_DIMENSION;
943         }
944       }
945       dimensionsCombinations.add(list);
946     }
947   }
948
949   /**
950    * Incompleto. Es para agregar incrementalmente dimensiones
951    * @param newDimensions
952    */

953   public void fillWithNewDimensions(LinkedList newDimensions) {
954     /* Iterator iterator;
955         LinkedList newDimensionsCombinations;
956         int dimension;
957
958         iterator = newDimensions.iterator();
959         while (iterator.hasNext()) {
960             dimension = ((Integer) iterator.next()).intValue();
961
962
963         }*/

964     ///todo: Falta completar resolucón incremental de dimensiones
965
}
966
967   /**
968    * Devuelve un iterador para recorrer los contenidos de la estructura Cube
969    * @return
970    */

971   public CubeIterator iterator() {
972     return CubeIterator.on(this);
973   }
974
975   /**
976    * Devuelve un iterador para recorrer los valores de una dimensión aplicando el criterio de ordenamiento
977    * @param table
978    * @param dimensionIndex
979    * @return
980    */

981   public Iterator sortedIteratorFor(HashMap table, int dimensionIndex) {
982     /*if ( getQuery().getDimensionRank()[dimensionIndex] > 0 ){
983       TreeMap treeMap = new TreeMap(getQuery().entryComparatorFor(dimensionIndex));
984       treeMap.entrySet().addAll(table.entrySet());
985       return treeMap.entrySet().iterator();
986     }
987     else{*/

988       TreeSet set;
989       set = new TreeSet(getQuery().entryComparatorFor(getQuery().getDimensions()[dimensionIndex]));
990       if (table!= null){
991         set.addAll(table.entrySet());
992       }
993       return set.iterator();
994     //}
995
}
996
997   /**
998    * Retorna un iterador ordenado sobre los valores para una dimensión a partir de las coordenadas indicadas por
999    * previousDimensions y values
1000   * @param dimension
1001   * @param previousDimensions
1002   * @param values
1003   * @return
1004   */

1005  public Iterator valuesFor(int dimension, int[] previousDimensions, Object JavaDoc[] values) {
1006    Object JavaDoc[] node;
1007    HashMap table;
1008
1009    node = measuresAtDimensionsValues(previousDimensions, values);
1010    table = (HashMap) node[dimension];
1011
1012    return sortedIteratorFor(table, dimension);
1013  }
1014
1015  public Set[] getDimensionValues() {
1016    if (dimensionValues == null) {
1017      dimensionValues = new TreeSet[getFullDimensionsSize()];
1018      for (int i = 0; i < dimensionValues.length; i++) {
1019        dimensionValues[i] = new TreeSet();
1020      }
1021    }
1022    return dimensionValues;
1023  }
1024
1025  private int getFullDimensionsSize() {
1026    return fullDimensionsSize;
1027  }
1028
1029  /**
1030   * Realiza las operaciones posteriores a la carga de datos del cubo. Se calculan los average y los maximos
1031   * y minimos para totales.
1032   */

1033  public void afterFill() {
1034    if(emptyCube()){
1035      root = newEmptyRoot();
1036    }else{
1037      int[] indexes = getAverageMetricsIndexes();
1038      if(indexes.length > 0){
1039        setAverageValues(root, indexes);
1040      }
1041      indexes = getMinMaxFootersIndexes();
1042      if(indexes.length > 0){
1043        int[] dimensionsOrder = getDimensionByGroupingOrder();
1044        for (int i = 0; i < dimensionsOrder.length; i++) {
1045          setMinMaxFooters(root, indexes, dimensionsOrder, 0);
1046        }
1047      }
1048      indexes = getCountDistinctIndexes();
1049      if(indexes.length > 0){
1050        setNodesCountValues(root, indexes);
1051      }
1052    }
1053    if(!excludedNodes.isEmpty()){
1054      Iterator it = dimensionsCombinations.iterator();
1055      while (it.hasNext()) {
1056        int[] dimensions = (int[])it.next();
1057        if(excludedNodes.containsKey(dimensions))
1058          addExcludedNode((Object JavaDoc[])excludedNodes.get(dimensions), dimensions);
1059      }
1060    }
1061    System.gc();
1062    System.gc();
1063    System.gc();
1064    System.gc();
1065    System.gc();
1066  }
1067
1068  private int[] getCountDistinctIndexes() {
1069    Object JavaDoc[] metrics = definition.getMetrics();
1070    LinkedList indexes = new LinkedList();
1071    for (int i = 0; i < metrics.length; i++) {
1072      Object JavaDoc metric = metrics[i];
1073      if(metric instanceof ReportMetricSpec){
1074        ReportMetricSpec metricSpec = (ReportMetricSpec)metric;
1075        if((metricSpec.getAggregateType().getType() == CalculationType.COUNT_DISTINCT_TYPE && isMetricInQuery(i))) {
1076          indexes.add(new Integer JavaDoc(i + dimensionsSize));
1077        }
1078      }
1079    }
1080    return toIntArray(indexes);
1081  }
1082
1083  private void setNodesCountValues(Object JavaDoc[] node, int[] indexes) {
1084    for (int j = 0; j < indexes.length; j++) {
1085      int index = indexes[j];
1086      node[index] = SharedFloat.newFrom(((Set)node[index]).size());
1087    }
1088    for (int i = 0; i < dimensionsSize; i++) {
1089      if(node[i]!=null){
1090        Map nodes = (Map)node[i];
1091        Iterator it = nodes.values().iterator();
1092        while (it.hasNext()) {
1093          Object JavaDoc[] childNode = (Object JavaDoc[]) it.next();
1094          setNodesCountValues(childNode, indexes);
1095        }
1096      }
1097    }
1098  }
1099
1100  /**
1101   * Obtiene las dimensiones existentes en la query en el orden en el que agrupan. Es decir, desde el primer nivel
1102   * de row en el que se agrupan todas las dimensiones, hacia las columnas que despliegan las metricas.
1103   * @return
1104   */

1105  private int[] getDimensionByGroupingOrder() {
1106    int[] result = new int[query.getDimensions().length];
1107    int[] queryColumns = query.getColumns();
1108    int[] queryRows = query.getRows();
1109    System.arraycopy(queryRows, 0, result, 0, queryRows.length);
1110    System.arraycopy(queryColumns, 0, result, queryRows.length, queryColumns.length);
1111    return result;
1112  }
1113
1114  /**
1115   * Retorna un nuevo nodo root vacío (solo con valores de metricas inicializados a 0).
1116   * @return
1117   */

1118  private Object JavaDoc[] newEmptyRoot() {
1119    Object JavaDoc[] result = new Object JavaDoc[arraySize];
1120    for (int i = dimensionsSize; i < result.length; i++) {
1121      result[i] = SharedFloat.newFrom(0);
1122    }
1123    return result;
1124  }
1125
1126  /**
1127   * Retorna si el cubo que se lleno esta vacio o no.
1128   * @return true si el cubo esta vacio.
1129   */

1130  private boolean emptyCube() {
1131    int i = 0;
1132    for (; i < dimensionsSize; i++) {
1133      if(root[i] != null){
1134        return false;
1135      }
1136    }
1137    for (; i < arraySize; i++){
1138      if(!Float.isNaN(((SharedFloat)root[i]).floatValue())){
1139        return false;
1140      }
1141    }
1142    return true;
1143  }
1144
1145  /**
1146   * Una vez que esta hecho el cubo, recorre los nodos para buscar los maximos y minimos para los footers de grupo
1147   * que tengan seleccionada alguna de estas funciones. Para ello recorre recursivamente el cubo desde el ultimo
1148   * nivel hacia el que mas agrupa, seteando en cada nivel los maximos y minimos correspondientes.
1149   * @param node el nodo que se esta recorriendo
1150   * @param indexes los indices de las metricas que tienen funciones max y min en sus footers
1151   * @param modelIndexes los indices del modelo, desde el que mas agrupa hasta el ultimo.
1152   * @param modelIndex indice dentro del que estoy ubicado dentro de modelIndexes (se referenciara el indice
1153   * de la dimension como modelIndexes[modelIndex])
1154   */

1155  private void setMinMaxFooters(Object JavaDoc[] node, int[] indexes, int[] modelIndexes, int modelIndex) {
1156    if(modelIndex + 1 < modelIndexes.length){
1157      Iterator iterator = ((Map)node[modelIndexes[modelIndex]]).entrySet().iterator();
1158      while (iterator.hasNext()) {
1159        Object JavaDoc[] childNode = (Object JavaDoc[])((Map.Entry) iterator.next()).getValue();
1160        for(int childIndex = modelIndex + 1; childIndex < modelIndexes.length; childIndex++){
1161          setMinMaxFooters(childNode, indexes, modelIndexes, childIndex);
1162        }
1163      }
1164    }
1165    int nodeIndex = getNodeIndex(node, modelIndexes);
1166    if(nodeIndex == modelIndexes[modelIndex]){
1167      for (int i = 0; i < indexes.length; i++) {
1168        int index = indexes[i];
1169        node[index] = obtainValue((Map)node[modelIndexes[modelIndex]], index);
1170      }
1171    }
1172    //Si el indice de la dimension esta en Rows y si hay columns, setea los valores maximos y minimos para columns
1173
if(isInRows(nodeIndex) && getQuery().getColumns()!=null && getQuery().getColumns().length > 0){
1174      setColumnValues(node, indexes, modelIndexes, nodeIndex);
1175    }
1176  }
1177
1178  /**
1179   * Dado un indice, retorna si ese indice se encuentra dentro de las row de la query.
1180   * @param dimensionIndex indice de la dimension que se busca
1181   * @return true si la dimension esta dentro de rows de la query
1182   */

1183  private boolean isInRows(int dimensionIndex) {
1184    for (int i = 0; i < getQuery().getRows().length; i++) {
1185      int row = getQuery().getRows()[i];
1186      if(dimensionIndex==row){
1187        return true;
1188      }
1189    }
1190    return false;
1191  }
1192
1193  /**
1194   * Setea los totales maximos y minimos para las columnas. Dentro del cube, un nodo que esta en row tiene el
1195   * despliegue de valores de las distintas combinaciones de column, para totales. Por ello, se deben recorrer
1196   * todos estos nodos, y buscar los valores maximos y minimos (se hace cuando se termino de cargar el cubo).
1197   * @param node nodo al que se le setean los maximos y minimos
1198   * @param indexes indices de las metricas que tienen maximos y minimos en footer.
1199   * @param modelIndexes indices de las dimensiones en el orden del modelo de la query
1200   * @param maxNodeIndex si el nodo agrupa en el nivel n, se pasara el indice de la dimension n+1 en orden de agrupacion
1201   */

1202  private void setColumnValues(Object JavaDoc[] node, int[] indexes, int[] modelIndexes, int maxNodeIndex) {
1203    for (int j = getQuery().getRows().length; j < modelIndexes.length; j++) {
1204      int modelIndex = modelIndexes[j];
1205      Iterator iterator = ((Map)node[modelIndex]).entrySet().iterator();
1206      while (iterator.hasNext()) {
1207        Map.Entry entry = (Map.Entry)iterator.next();
1208        Vector dimensionsValues = new Vector();
1209        dimensionsValues.add(entry.getKey());
1210        Object JavaDoc[] childNode = (Object JavaDoc[])entry.getValue();
1211        for (int i = 0; i < indexes.length; i++) {
1212          int footerIndex = indexes[i];
1213          childNode[footerIndex] = getFooterValueFor(node, dimensionsValues, footerIndex, modelIndexes, j, maxNodeIndex);
1214        }
1215        setSubColumnValues(node, childNode, dimensionsValues, indexes, modelIndexes, j + 1, maxNodeIndex, j);
1216      }
1217    }
1218  }
1219
1220  /**
1221   * Setea los valores de todos los nodos intermedios en column recorriendolos recursivamente desde el que mas agrupa
1222   * al que menos (el que despliega en las metricas).
1223   * @param node nodo de Row al que se le estan seteando los valores
1224   * @param childNode nodo superior del modelo de column
1225   * @param dimensionsValues valores agrupados de los nodos anteriores de column
1226   * @param indexes indices de las metricas que usan max y min en totales
1227   * @param modelIndexes indices de las dimensiones en el orden del modelo de la query
1228   * @param columnIndex indice de la columna dentro de los model indexes (se referenciara modelIndexes[columnIndex]).
1229   * @param maxNodeIndex si el nodo agrupa en el nivel n, se pasara el indice de la dimension n+1 en orden de agrupacion
1230   * @param startingColumnIndex primera dimension en column que se esta considerando en la agrupacion de dimensionsValues
1231   */

1232  private void setSubColumnValues(Object JavaDoc[] node, Object JavaDoc[] childNode, Vector dimensionsValues, int[] indexes, int[] modelIndexes, int columnIndex, int maxNodeIndex, int startingColumnIndex) {
1233    if(columnIndex < modelIndexes.length){
1234      Iterator iterator = ((Map)childNode[modelIndexes[columnIndex]]).entrySet().iterator();
1235      while (iterator.hasNext()) {
1236        Map.Entry entry = (Map.Entry)iterator.next();
1237        Vector clonedValues = (Vector)dimensionsValues.clone();
1238        clonedValues.add(entry.getKey());
1239        Object JavaDoc[] subNode = (Object JavaDoc[])entry.getValue();
1240        for (int i = 0; i < indexes.length; i++) {
1241          int footerIndex = indexes[i];
1242          subNode[footerIndex] = getFooterValueFor(node, clonedValues, footerIndex, modelIndexes, startingColumnIndex, maxNodeIndex);
1243        }
1244        setSubColumnValues(node, subNode, clonedValues, indexes, modelIndexes, columnIndex + 1, maxNodeIndex, startingColumnIndex);
1245      }
1246    }
1247  }
1248
1249  /**
1250   * Obtiene el valor maximo o minimo para una column segun los valores de su nodo row
1251   * @param node nodo row que se esta considerando
1252   * @param dimensionValues valores agrupados de las dimensiones en column. Se agrupan los valores desde el nivel inicial
1253   * @param index indice de la metrica
1254   * @param modelIndexes indices de las dimensiones en el orden del modelo de la query
1255   * @param columnIndex indice de la columna dentro de los model indexes (se referenciara modelIndexes[columnIndex]).
1256   * @param maxNodeIndex si el nodo agrupa en el nivel n, se pasara el indice de la dimension n+1 en orden de agrupacion
1257   * @return
1258   */

1259  private SharedFloat getFooterValueFor(Object JavaDoc[] node, Vector dimensionValues, int index, int[] modelIndexes, int columnIndex, int maxNodeIndex) {
1260    SharedFloat result = SharedFloat.newFrom(Float.NaN);
1261    Iterator iterator = ((Map)node[maxNodeIndex]).entrySet().iterator();
1262    while (iterator.hasNext()) {
1263      Map.Entry entry = (Map.Entry)iterator.next();
1264      Map subNodeValues = (Map)((Object JavaDoc[])entry.getValue())[modelIndexes[columnIndex]];
1265      Object JavaDoc[] columnNode = (Object JavaDoc[])subNodeValues.get(dimensionValues.elementAt(0));
1266      for (int i = 1; i < dimensionValues.size() && columnNode!=null && columnIndex + i < modelIndexes.length; i++) {
1267        Object JavaDoc dimensionValue = dimensionValues.elementAt(i);
1268        subNodeValues = (Map)columnNode[modelIndexes[columnIndex+i]];
1269        columnNode = (Object JavaDoc[])subNodeValues.get(dimensionValue);
1270      }
1271      if(columnNode!=null){
1272        result = (SharedFloat)groupFooterStrategies[index - dimensionsSize].operate(columnNode, index, result, null);
1273      }else{
1274        //Simula pasarle un nodo por parametro. Dentro del stategy accede a object[0], y le dara el mismo valor.
1275
result = (SharedFloat)groupFooterStrategies[index - dimensionsSize].operate(new Object JavaDoc[]{result}, 0, null, null);
1276      }
1277    }
1278    return result;
1279  }
1280
1281  /**
1282   * Obtiene el mayor indice de grupo de un nodo. Por ejemplo si un estamos considerando un nodo que tiene los valores
1283   * de la primer dimension en row, retornará el indice de la segunda dimension en row.
1284   * @param node
1285   * @param modelIndexes
1286   * @return
1287   */

1288  private int getNodeIndex(Object JavaDoc[] node, int[] modelIndexes) {
1289    for (int i = 0; i < modelIndexes.length; i++) {
1290      int modelIndex = modelIndexes[i];
1291      if(node[modelIndex]!=null){
1292        return modelIndex;
1293      }
1294    }
1295    return modelIndexes[0];
1296  }
1297
1298  /**
1299   * Dado un conjunto de nodos y un indice de metrica, retorna el valor para el total de esa metrica, calculando,
1300   * segun corresponda a la metrica, maximo o minimo.
1301   * @param nodeValues subnodos
1302   * @param index indice de la metrica
1303   * @return
1304   */

1305  private SharedFloat obtainValue(Map nodeValues, int index) {
1306    SharedFloat result = SharedFloat.newFrom(Float.NaN);
1307    for (Iterator iterator = nodeValues.entrySet().iterator(); iterator.hasNext();) {
1308      Object JavaDoc[] node = (Object JavaDoc[])((Map.Entry) iterator.next()).getValue();
1309      result = (SharedFloat)groupFooterStrategies[index - dimensionsSize].operate(node, index, result, null);
1310    }
1311    return result;
1312  }
1313
1314  /**
1315   * Busca los indices de las metricas que utilizan maximos y minimos
1316   * @return
1317   */

1318  private int[] getMinMaxFootersIndexes() {
1319    Object JavaDoc[] metrics = definition.getMetrics();
1320    LinkedList indexes = new LinkedList();
1321    for (int i = 0; i < metrics.length; i++) {
1322      Object JavaDoc metric = metrics[i];
1323      if(metric instanceof ReportMetricSpec){
1324        ReportMetricSpec metricSpec = (ReportMetricSpec)metric;
1325        if((metricSpec.getGroupFooterType().getType() == CalculationType.MAX_TYPE ||
1326                metricSpec.getGroupFooterType().getType() == CalculationType.MIN_TYPE) &&
1327                isMetricInQuery(i)) {
1328          indexes.add(new Integer JavaDoc(i + dimensionsSize));
1329        }
1330      }
1331    }
1332    return toIntArray(indexes);
1333  }
1334
1335  /**
1336   * Setea los valores promedio para un nodo
1337   * @param node nodo a setear
1338   * @param indexes indices de las metricas que utilizan average
1339   */

1340  private void setAverageValues(Object JavaDoc[] node, int[] indexes) {
1341    setAverageValueForNode(node, indexes);
1342    int[] dimensions = query.getDimensions();
1343    for (int i = 0; i < dimensions.length; i++) {
1344      int dimension = dimensions[i];
1345      if(node[dimension]!=null){
1346        setAverageValueForChild((Map)node[dimension], indexes);
1347      }
1348    }
1349  }
1350
1351  /**
1352   * Recorre los subnodos de un nodo, seteando recursivamente los valores promedio
1353   * @param map subnodos
1354   * @param indexes
1355   */

1356  private void setAverageValueForChild(Map map, int[] indexes) {
1357    for (Iterator iterator = map.entrySet().iterator(); iterator.hasNext();) {
1358      Map.Entry entry = (Map.Entry) iterator.next();
1359      setAverageValues((Object JavaDoc[])entry.getValue(), indexes);
1360    }
1361  }
1362
1363  /**
1364   * Setea especificamente el valor del average de un nodo, diferenciando si este es o no total.
1365   * @param node
1366   * @param indexes
1367   */

1368  private void setAverageValueForNode(Object JavaDoc[] node, int[] indexes) {
1369    for (int i = 0; i < indexes.length; i++) {
1370      int index = indexes[i];
1371      if(isLastLevel(node) && metricStrategies[index] instanceof AverageStrategy){
1372        node[index + dimensionsSize] = ((SharedFloat)node[index + dimensionsSize]).div((SharedFloat)node[index + dimensionsSize + metricsSize/2]);
1373      }else if(groupFooterStrategies[index] instanceof AverageStrategy){
1374        node[index + dimensionsSize] = ((SharedFloat)node[index + dimensionsSize]).div((SharedFloat)node[index + dimensionsSize + metricsSize/2]);
1375      }
1376    }
1377  }
1378
1379  /**
1380   * Retorna verdadero si el nodo corresponde al ultimo nivel de grupo, es decir, no agrupa a otras dimensiones.
1381   * @param node
1382   * @return
1383   */

1384  private boolean isLastLevel(Object JavaDoc[] node) {
1385    for (int i = 0; i < node.length && i < getDimensionsSize(); i++) {
1386      if(node[i]!=null){
1387        return false;
1388      }
1389    }
1390    return true;
1391  }
1392
1393  public Object JavaDoc[] getMetricsValuesAt(int[] dimensions, Object JavaDoc[] values) {
1394    Object JavaDoc[] node;
1395    int index;
1396    int dimensionsLenght;
1397
1398    node = root;
1399    dimensionsLenght = dimensions.length;
1400    for (index = 0; index < dimensionsLenght; index++) {
1401      node = atDimensionValueFor(values[index], dimensions[index], node);
1402    }
1403    return node;
1404  }
1405
1406  private Object JavaDoc[] atDimensionValueFor(Object JavaDoc value, int dimension, Object JavaDoc[] node) {
1407    HashMap dict;
1408    Object JavaDoc o;
1409    if(node==null){
1410      return node;
1411    }
1412    if ((node[dimension] instanceof HashMap)) {
1413      dict = (HashMap) node[dimension];
1414    }
1415    else {
1416      return null;
1417    }
1418    o = dict.get(value);
1419    return (Object JavaDoc[]) o;
1420  }
1421
1422  public Set getDimensionValues(int index) throws InfoException {
1423    if(getDimensionValues()[index].isEmpty() && pivot!=null){
1424      getDimensionValues()[index].addAll(pivot.getDimensionValues(index));
1425    }
1426    return getDimensionValues()[index];
1427  }
1428
1429  protected void setPivot(Pivot pivot) {
1430    this.pivot = pivot;
1431  }
1432
1433  public MetricCalculationStrategy[] getMetricStrategies() {
1434    return metricStrategies;
1435  }
1436
1437}
1438
Popular Tags