Source Code Cross Referenced for Cube.java in  » Report » jmagallanes-1.0 » com » calipso » reportgenerator » reportcalculator » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Report » jmagallanes 1.0 » com.calipso.reportgenerator.reportcalculator 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        package com.calipso.reportgenerator.reportcalculator;
0002:
0003:        import com.calipso.reportgenerator.common.InfoException;
0004:
0005:        import java.util.*;
0006:        import java.io.Serializable;
0007:        import java.io.ObjectOutputStream;
0008:        import java.io.IOException;
0009:        import java.io.ObjectInputStream;
0010:
0011:        import com.calipso.reportgenerator.common.ReportMetricSpec;
0012:        import com.calipso.reportgenerator.common.LanguageTraslator;
0013:        import com.calipso.reportgenerator.reportdefinitions.types.CalculationType;
0014:
0015:        /**
0016:         * La clase Cube representa la información de un reporte.
0017:         * Podríamos verlo como el resultado de la aplicación de una
0018:         * query a un Pivot. La estructura del Cube es un árbol.
0019:         * En la raíz del mismo hay un array que contiene una serie de
0020:         * dimensiones seguida de las métricas.
0021:         * Mientras en las métricas está acumulado el total de cada una, en las
0022:         * dimensiones y diccionarios.  Cada uno de estos diccionarios contiene, como
0023:         * clave, los distintos valores para la dimension correspondiente (la posición
0024:         * en el array).  Como valor, los diccionarios contienen un nuevo array similar
0025:         * al de la raíz.
0026:         * El resultado es una estructura arbórea que alterna nodos tipo array con
0027:         * diccionarios.
0028:         * Por cuestiones de performance se ha utilizado la clase SharedFloat en lugar
0029:         * de los Float tradicionales
0030:         * @see	    com.calipso.reportgenerator.reportcalculator.CubeQuery
0031:         * @see	    Pivot
0032:         * @see	    SharedFloat
0033:         */
0034:
0035:        public class Cube implements  Serializable, PivotClient {
0036:            private CubeDefinition definition;
0037:            private CubeQuery query;
0038:            private Object[] root;
0039:            private int arraySize;
0040:            private int metricsSize;
0041:            private LinkedList dimensionsCombinations;
0042:            private Set[] dimensionValues;
0043:            private MetricCalculationStrategy[] metricStrategies;
0044:            private MetricCalculationStrategy[] groupFooterStrategies;
0045:            private int dimensionsSize;
0046:            private int[] queryMetrics;
0047:            private static final int INVALID_DIMENSION = 999;
0048:            private Object[] metrics;
0049:            private Map excludedNodes = new HashMap();
0050:            private static final String others = LanguageTraslator.traslate(
0051:                    "404").intern();//SharedString.newFrom(LanguageTraslator.traslate("404"));
0052:            private Pivot pivot;
0053:            private int fullDimensionsSize;
0054:
0055:            /**
0056:             * Retorna un entero que determina la cantidad de dimensiones
0057:             * @return
0058:             */
0059:            public int getDimensionsSize() {
0060:                return dimensionsSize;
0061:            }
0062:
0063:            /**
0064:             * Inicializa la cantidad de dimensiones y métricas del Cube
0065:             * a partir de la cantidad de dimensiones y métricas de la definicion.
0066:             * Inicializa la variable arraySize de acuerdo a la suma de metricas
0067:             * y dimensiones, es decir, el total de columnas.
0068:             * Tambien inicializa las formas de calculo de metricas y totales. Inicializa las
0069:             * metricas auxiliares para el cálculo de promedios.
0070:             */
0071:            private void initialize() {
0072:                metrics = definition.getMetrics();
0073:                initAverageCountMetrics();
0074:                metricsSize = getMetrics().length;
0075:                dimensionsSize = query.getDimensions().length;//definition.getDimensions().length;
0076:                fullDimensionsSize = definition.getDimensions().length;
0077:                setArraySize(dimensionsSize + metricsSize);
0078:                initMetricStrategies();
0079:                initGroupFooterStrategies();
0080:                excludedNodes = new HashMap();
0081:            }
0082:
0083:            /**
0084:             * Inicializa las metricas adicionales que se usaran para el calculo de los promedios. Por cada metrica crea
0085:             * una adicional que contara las ocurrencias.
0086:             */
0087:            private void initAverageCountMetrics() {
0088:                if (this .hasAverageMetrics()) {
0089:                    ReportMetricSpec countMetric;
0090:                    Object[] resultMetrics = new Object[(metrics.length * 2)];
0091:                    for (int i = 0; i < metrics.length; i++) {
0092:                        resultMetrics[i] = metrics[i];
0093:                        countMetric = new ReportMetricSpec("Count" + i);
0094:                        countMetric.setAggregateType(CalculationType.COUNT);
0095:                        countMetric.setGroupFooterType(CalculationType.COUNT);
0096:                        resultMetrics[metrics.length + i] = countMetric;
0097:                    }
0098:                    metrics = resultMetrics;
0099:                }
0100:            }
0101:
0102:            /**
0103:             * Retorna verdadero si existen metricas que utilizan average.
0104:             * @return
0105:             */
0106:            private boolean hasAverageMetrics() {
0107:                return getAverageMetricsIndexes().length > 0;
0108:            }
0109:
0110:            /**
0111:             * Busca las metricas que utilizan average como tipo de calculo. Arma un array con los indices de dichas metricas.
0112:             * @return un array con los indices de las metricas que utilizan average
0113:             */
0114:            private int[] getAverageMetricsIndexes() {
0115:                Object[] metrics = definition.getMetrics();
0116:                LinkedList indexes = new LinkedList();
0117:                for (int i = 0; i < metrics.length; i++) {
0118:                    Object metric = metrics[i];
0119:                    if (metric instanceof  ReportMetricSpec) {
0120:                        ReportMetricSpec metricSpec = (ReportMetricSpec) metric;
0121:                        if ((metricSpec.getAggregateType().getType() == CalculationType.AVERAGE_TYPE || metricSpec
0122:                                .getGroupFooterType().getType() == CalculationType.AVERAGE_TYPE)
0123:                                && isMetricInQuery(i)) {
0124:                            indexes.add(new Integer(i));
0125:                        }
0126:                    }
0127:                }
0128:                return toIntArray(indexes);
0129:            }
0130:
0131:            /**
0132:             * Retorna true si la metrica pasada por parametro esta en la query
0133:             * @param i indice de la metrica que se busca en la query
0134:             * @return true si la metrica esta en la query
0135:             */
0136:            private boolean isMetricInQuery(int i) {
0137:                for (int j = 0; j < queryMetrics.length; j++) {
0138:                    if (i + definition.getDimensions().length == queryMetrics[j]) {
0139:                        return true;
0140:                    }
0141:                }
0142:                return false;
0143:            }
0144:
0145:            /**
0146:             * Inicializa el array con las estrategias de calculo de los totales tomadas de los ReportMetricSpec de cada metrica.
0147:             */
0148:            private void initGroupFooterStrategies() {
0149:                Object[] metrics = getMetrics();
0150:                groupFooterStrategies = new MetricCalculationStrategy[metrics.length];
0151:                for (int i = 0; i < metrics.length; i++) {
0152:                    Object metric = metrics[i];
0153:                    if (metric instanceof  ReportMetricSpec) {
0154:                        groupFooterStrategies[i] = MetricCalculationStrategy
0155:                                .getFooterStrategyFor((ReportMetricSpec) metric);
0156:                    } else {
0157:                        groupFooterStrategies[i] = new SumStrategy();
0158:                    }
0159:                }
0160:            }
0161:
0162:            /**
0163:             * Retorna las metricas del cubo. Normalmente seran las metricas que esten en el definition, pero si
0164:             * existen metricas que calculen AVERAGE las metricas estaran duplicadas (dicha replica contara
0165:             * realizara un COUNT para porder tener la cantidad sobre la cual dividir).
0166:             * @return
0167:             */
0168:            private Object[] getMetrics() {
0169:                return metrics;
0170:            }
0171:
0172:            /**
0173:             * Inicializa las estrategias de calculo para cada metrica.
0174:             */
0175:            private void initMetricStrategies() {
0176:                Object[] metrics = getMetrics();
0177:                metricStrategies = new MetricCalculationStrategy[metrics.length];
0178:                for (int i = 0; i < metrics.length; i++) {
0179:                    Object metric = metrics[i];
0180:                    if (metric instanceof  ReportMetricSpec) {
0181:                        metricStrategies[i] = MetricCalculationStrategy
0182:                                .getMetricStrategyFor((ReportMetricSpec) metric);
0183:                    } else {
0184:                        metricStrategies[i] = new SumStrategy();
0185:                    }
0186:                }
0187:            }
0188:
0189:            /**
0190:             * Retorna la definicion del Cube
0191:             * @return
0192:             */
0193:            public CubeDefinition getDefinition() {
0194:                return definition;
0195:            }
0196:
0197:            /**
0198:             * Asigna al Cube la definición de Cube correspondiente
0199:             * @param definition
0200:             */
0201:            public void setDefinition(CubeDefinition definition) {
0202:                this .definition = definition;
0203:            }
0204:
0205:            /**
0206:             * Retorna la Query del Cube
0207:             * @return
0208:             */
0209:            public CubeQuery getQuery() {
0210:                return query;
0211:            }
0212:
0213:            /**
0214:             * Asigna la nueva query a ejecutar
0215:             * @param query
0216:             * @throws InfoException Si se produjo un error en el cálculo del cubo.
0217:             */
0218:            public void setQuery(CubeQuery query) throws InfoException {
0219:                try {
0220:                    this .query = query;
0221:                    queryMetrics = getQuery().getMetrics();
0222:                    setDimensionsCombinations();
0223:                } catch (Exception e) {
0224:                    throw new InfoException(
0225:                            com.calipso.reportgenerator.common.LanguageTraslator
0226:                                    .traslate("102"), e);
0227:                }
0228:            }
0229:
0230:            /**
0231:             * Retorna un array que representa la raíz del cube
0232:             * y que contiene una serie de dimensiones seguidas de las métricas
0233:             * @return
0234:             */
0235:            public Object[] getRoot() {
0236:                return root;
0237:            }
0238:
0239:            /**
0240:             * Asigna un array que representa la raíz del cube y que contiene
0241:             * una serie de dimensiones seguidas de las métricas
0242:             * @param root
0243:             */
0244:            public void setRoot(Object[] root) {
0245:                this .root = root;
0246:            }
0247:
0248:            /**
0249:             * Retorna arraySize que es un entero que
0250:             * representa la suma de dimensiones y métricas, es decir el total de columnas del Cube.
0251:             * @return
0252:             */
0253:            public int getArraySize() {
0254:                return arraySize;
0255:            }
0256:
0257:            /**
0258:             * Inicializa arraySize a partir de un entero que
0259:             * representa la suma de dimensiones y métricas, es decir el total de columnas del Cube.
0260:             * @param arraySize
0261:             */
0262:            public void setArraySize(int arraySize) {
0263:                this .arraySize = arraySize;
0264:            }
0265:
0266:            /**
0267:             * Retorna la cantidad total de métricas del Cube.
0268:             * @return
0269:             */
0270:            public int getMetricsSize() {
0271:                return metricsSize;
0272:            }
0273:
0274:            /**
0275:             * Crea todas las combinaciones de dimensiones necesarias para cualquier tipo de totales
0276:             * @throws InfoException
0277:             */
0278:            private void setDimensionsCombinations() throws InfoException {
0279:                dimensionsCombinations = new LinkedList();
0280:                int[] cols = query.getColumns();
0281:                int[] rows = query.getRows();
0282:                for (int i = 1; i <= rows.length; i++) {
0283:                    int[] list = new int[i];
0284:                    for (int j = 0; j < i; j++) {
0285:                        list[j] = j;
0286:                    }
0287:                    dimensionsCombinations.add(list);
0288:                    for (int z = 1; z <= cols.length; z++) {
0289:                        int[] withCol = new int[i + z];
0290:                        System.arraycopy(list, 0, withCol, 0, list.length);
0291:                        for (int j = 0; j < z; j++) {
0292:                            withCol[j + i] = rows.length + j;
0293:                        }
0294:                        dimensionsCombinations.add(withCol);
0295:                    }
0296:                }
0297:                if (rows.length != 0) {
0298:                    for (int i = 1; i <= cols.length; i++) {
0299:                        int[] list = new int[i];
0300:                        for (int j = 0; j < i; j++) {
0301:                            list[j] = rows.length + j;
0302:                        }
0303:                        dimensionsCombinations.add(list);
0304:                    }
0305:                }
0306:                /*int[] dimensions;
0307:                int factor;
0308:                int[] list;
0309:                int queryDimensionsSize;
0310:                int combination;
0311:                String bits;
0312:                int bitsLenght;
0313:                int pos;
0314:                try{
0315:                  dimensions = getIntegerArray(getQuery().getDimensions().length);
0316:                  queryDimensionsSize = dimensions.length;
0317:                  dimensionsCombinations = new LinkedList();
0318:                  for (combination = 1; combination <= (Math.pow(2, queryDimensionsSize) - 1); combination++) {
0319:                  bits = Integer.toBinaryString(combination);
0320:                  bitsLenght = bits.length();
0321:                  list = new int[bitsLenght];
0322:                  for (int index = 0; index < bitsLenght; index++) {
0323:                    pos = bitsLenght - 1 - index;
0324:                    factor = Character.digit(bits.charAt(pos), 10);
0325:                    if (factor == 1) {
0326:                      list[index] = factor * dimensions[index];
0327:                    }
0328:                    else {
0329:                      list[index] = INVALID_DIMENSION;
0330:                    }
0331:                  }
0332:                    //if(list[0]==0){
0333:                      dimensionsCombinations.add(list);
0334:                    //}
0335:                  }
0336:                }
0337:                catch (Exception e){
0338:                  throw new InfoException(com.calipso.reportgenerator.common.LanguageTraslator.traslate("103"),e);
0339:                } */
0340:            }
0341:
0342:            public int[] getIntegerArray(int base, int length) {
0343:                int[] result = new int[length];
0344:                for (int j = 0, i = base; j < result.length; i++, j++) {
0345:                    result[j] = i;
0346:                }
0347:                return result;
0348:            }
0349:
0350:            /**
0351:             * Reinicializa el Cube. Como efecto se pierden todos los datos actuales.
0352:             */
0353:            public void reset() {
0354:                initialize();
0355:                root = null;
0356:                System.gc();
0357:                root = newArray();
0358:            }
0359:
0360:            /**
0361:             * Crea un array para un nodo con el tamaño correspondiente según la cantidad de dimensiones y métricas
0362:             * inicializando los valores de las métricas con 0
0363:             *
0364:             * @return
0365:             */
0366:            private Object[] newArray() {
0367:                Object[] result;
0368:                int index;
0369:                result = new Object[arraySize];
0370:                for (index = dimensionsSize; index < arraySize; index++) {
0371:                    result[index] = SharedFloat.newFrom(Float.NaN);
0372:                }
0373:
0374:                return result;
0375:            }
0376:
0377:            /**
0378:             * Agrega row si pasa por los filtros
0379:             * @param row
0380:             */
0381:            public void fillWith(Object[] row) {
0382:                if (query.matches(row)) {
0383:                    fillDimensionValues(row);
0384:                    if (query.valuesEnabled(row)) {
0385:                        row = shrinkRow(row);
0386:                        basicFillWith(row);
0387:                    }
0388:                } else if (query.isGroupExcludedValues()) {
0389:                    if (query.otherFilterMatches()) {
0390:                        row = shrinkRow(row);
0391:                        fillExcludedValueNode(row);
0392:                    }
0393:                }
0394:            }
0395:
0396:            /**
0397:             * Selecciona de row solo las dimensiones necesarias y las ordena
0398:             * @param row
0399:             * @return
0400:             */
0401:            private Object[] shrinkRow(Object[] row) {
0402:                int[] dimensions = query.getDimensions();
0403:                int[] metrics = query.getMetrics();
0404:                Object[] result = new Object[dimensions.length + metrics.length];
0405:                int i = 0;
0406:                for (; i < dimensions.length; i++) {
0407:                    result[i] = row[dimensions[i]];
0408:                }
0409:                for (int j = 0; j < metrics.length; i++, j++) {
0410:                    result[i] = row[metrics[j]];
0411:                }
0412:                return result;
0413:            }
0414:
0415:            private void fillExcludedValueNode(Object[] aRow) {
0416:                Object[] measures;
0417:                Iterator iterator;
0418:                //EnumerationCubeFilter filter = query.getExcludeGroupFilter();
0419:                measures = valuesOfFrom(queryMetrics, aRow);
0420:                iterator = dimensionsCombinations.iterator();
0421:                while (iterator.hasNext()) {
0422:                    /* Agarro dimension por dimension. Me fijo si el valor de esa dimension en
0423:                    este row esta o va a otros.
0424:                    Si esta, busco su nodo y la agrego. Pero en los subnodos me tengo que fijar
0425:                    si los valores de esas dimensiones estan o van en otros.
0426:                    Si va a otros, agrego al nodo de otros de esta dimension, pero nuevamente
0427:                    en los subnodos debo ver si esta el valor o va a otros.
0428:                     */
0429:                    excludedAdd((int[]) iterator.next(), aRow, measures);
0430:                }
0431:                addTotal(measures, root, aRow);
0432:            }
0433:
0434:            private void excludedAdd(int[] dimensions, Object[] aRow,
0435:                    Object[] measures) {
0436:                Object[] node;
0437:                node = getExcludedNode(dimensions, aRow);
0438:                if (isLastLevel(dimensions)) {
0439:                    addTo(measures, node, aRow);
0440:                } else {
0441:                    addTotal(measures, node, aRow);
0442:                }
0443:            }
0444:
0445:            private Object[] getExcludedNode(int[] dimensions, Object[] aRow) {
0446:                Object[] node = root;
0447:                for (int i = 0; i < dimensions.length; i++) {
0448:                    int dimension = dimensions[i];
0449:                    if (dimension != INVALID_DIMENSION) {
0450:                        if (node[dimension] == null) {
0451:                            node[dimension] = new HashMap();
0452:                        }
0453:                        Object value;
0454:                        //Verifica si el filtro que excluye valores tiene la dimension, y luego que el valor este excluido (no este en el filtro).
0455:                        if (query.getExcludeGroupFilter().hasDimension(
0456:                                dimension)
0457:                                && !query.getExcludeGroupFilter()
0458:                                        .containsValue(dimension,
0459:                                                aRow[dimension])) {
0460:                            value = others;
0461:                        } else {
0462:                            value = aRow[dimension];
0463:                        }
0464:                        if (!((HashMap) node[dimension]).containsKey(value)) {
0465:                            ((HashMap) node[dimension]).put(value, newArray());
0466:                        }
0467:                        node = (Object[]) ((HashMap) node[dimension])
0468:                                .get(value);
0469:                    }
0470:                }
0471:                /*Iterator it = excludedNodes.entrySet().iterator();
0472:                Object[] excludedNode = null;
0473:                while(it.hasNext() && excludedNode==null){
0474:                  Map.Entry entry = (Map.Entry)it.next();
0475:                  if(Arrays.equals((int [])entry.getKey(), dimensions)){
0476:                    excludedNode = (Object[])entry.getValue();
0477:                  }
0478:                }
0479:                if(excludedNode==null){
0480:                  excludedNode = newExcludedNode(dimensions);
0481:                  excludedNodes.put(dimensions,  excludedNode);
0482:                  //addExcludedNode(excludedNode, dimensions);
0483:                }
0484:                return excludedNode;*/
0485:                return node;
0486:            }
0487:
0488:            private void addExcludedNode(Object[] excludedNode, int[] dimensions) {
0489:                Object[] node = root;
0490:                for (int i = 0; i < dimensions.length - 1; i++) {
0491:                    int dimension = dimensions[i];
0492:                    if (dimension != INVALID_DIMENSION) {
0493:                        HashMap map = (HashMap) node[dimension];
0494:                        if (map.containsKey(others)) {
0495:                            node = (Object[]) map.get(others);
0496:                        }
0497:                    }
0498:                }
0499:                Object child = node[dimensions[dimensions.length - 1]];
0500:                if (child == null) {
0501:                    child = new HashMap();
0502:                    node[dimensions[dimensions.length - 1]] = child;
0503:                }
0504:                ((Map) child).put(others, excludedNode);
0505:            }
0506:
0507:            private Object[] newExcludedNode(int[] dimensions) {
0508:                Object[] node = newArray();
0509:                for (int i = 0; i < dimensions.length; i++) {
0510:                    int dimension = dimensions[i];
0511:                    if (dimension != INVALID_DIMENSION) {
0512:                        HashMap dict = new HashMap();
0513:                        Object[] subNode = getSubNode(dimensions, i + 1);
0514:                        dict.put(others, subNode);
0515:                        node[dimension] = dict;
0516:                    }
0517:                }
0518:                return node;
0519:            }
0520:
0521:            private Object[] getSubNode(int[] dimensions, int index) {
0522:                Object[] node = newArray();
0523:                for (int j = index; j < dimensions.length; j++) {
0524:                    int dimension = dimensions[j];
0525:                    if (dimension != INVALID_DIMENSION) {
0526:                        HashMap dict = new HashMap();
0527:                        Object[] subNode = getSubNode(dimensions, j + 1);
0528:                        dict.put(others, subNode);
0529:                        node[dimension] = dict;
0530:                    }
0531:                }
0532:                return node;
0533:            }
0534:
0535:            /**
0536:             * Resuelve la inclusión de un nuevo row actualizando los totales correspondientes
0537:             * @param aRow
0538:             */
0539:            protected void basicFillWith(Object[] aRow) {
0540:                Object[] measures;
0541:                Iterator iterator;
0542:                measures = valuesOfFrom(queryMetrics, aRow);
0543:                iterator = dimensionsCombinations.iterator();
0544:                while (iterator.hasNext()) {
0545:                    atFromAdd((int[]) iterator.next(), aRow, measures);
0546:                }
0547:                addTotal(measures, root, aRow);
0548:            }
0549:
0550:            private void fillDimensionValues(Object[] row) {
0551:                int[] dims;
0552:                dims = query.getRows();
0553:                fillDimensionValuesFromArray(dims, row);
0554:                dims = query.getColumns();
0555:                fillDimensionValuesFromArray(dims, row);
0556:                dims = query.getPages();
0557:                fillDimensionValuesFromArray(dims, row);
0558:            }
0559:
0560:            private void fillDimensionValuesFromArray(int[] dimensions,
0561:                    Object[] row) {
0562:                for (int i = 0; i < dimensions.length; i++) {
0563:                    int dim = dimensions[i];
0564:                    if (row[dim] != null) {
0565:                        getDimensionValues()[dim].add(row[dim]);
0566:                    }
0567:                }
0568:            }
0569:
0570:            /**
0571:             * Resuelve la operación de sumarización
0572:             * @param measures
0573:             * @param node
0574:             */
0575:            private void addTo(Object[] measures, Object[] node, Object[] aRow) {
0576:                int index;
0577:                for (index = 0; index < metricsSize; index++) {
0578:                    node[index + dimensionsSize] = (metricStrategies[index])
0579:                            .operate(node, index + dimensionsSize,
0580:                                    measures[index], aRow);
0581:                    //((SharedFloat) node[index + dimensionsSize]).add(measures[index]);
0582:                }
0583:            }
0584:
0585:            /**
0586:             * Suma los measures en las coordenadas dadas por dimensions y el contenido de aRow
0587:             * @param dimensions
0588:             * @param aRow
0589:             * @param measures
0590:             */
0591:            private void atFromAdd(int[] dimensions, Object[] aRow,
0592:                    Object[] measures) {
0593:                Object[] node;
0594:                node = atFrom(dimensions, aRow);
0595:                if (node != null) {
0596:                    if (isLastLevel(dimensions)) {
0597:                        addTo(measures, node, aRow);
0598:                    } else {
0599:                        addTotal(measures, node, aRow);
0600:                    }
0601:                }
0602:            }
0603:
0604:            /**
0605:             * Agrega valores totales al cubo, realizando el calculo segun la estrategia de total que tenga la metrica.
0606:             * @param measures los valores a agregar
0607:             * @param node el nodo donde se opera
0608:             */
0609:            private void addTotal(Object[] measures, Object[] node,
0610:                    Object[] aRow) {
0611:                for (int index = 0; index < metricsSize; index++) {
0612:                    node[index + dimensionsSize] = (groupFooterStrategies[index])
0613:                            .operate(node, index + dimensionsSize,
0614:                                    measures[index], aRow);
0615:                }
0616:            }
0617:
0618:            /**
0619:             * Comprueba si las dimensiones especificadas darian un nodo final (de ultimo nivel) o si es un nodo que agrupa.
0620:             * @param dimensions
0621:             * @return
0622:             */
0623:            private boolean isLastLevel(int[] dimensions) {
0624:                if (dimensions.length < getQuery().getDimensions().length) {
0625:                    return false;
0626:                }
0627:                for (int i = 0; i < dimensions.length; i++) {
0628:                    int dimension = dimensions[i];
0629:                    if (dimension == INVALID_DIMENSION) {
0630:                        return false;
0631:                    }
0632:                }
0633:                return true;
0634:            }
0635:
0636:            /**
0637:             * Retorna los valores para las coordenadas dadas por dimensions y el contenido de aRow
0638:             * Va recorriendo recursivamente los nodos del tipo array hasta agotar las dimensions
0639:             * @param dimensions
0640:             * @param aRow
0641:             * @return
0642:             */
0643:            private Object[] atFrom(int[] dimensions, Object[] aRow) {
0644:                Object[] node;
0645:                int index;
0646:                int dimension;
0647:                int dimensionsLenght;
0648:                Object value;
0649:
0650:                node = root;
0651:                dimensionsLenght = dimensions.length;
0652:                for (index = 0; index < dimensionsLenght; index++) {
0653:                    dimension = dimensions[index];
0654:                    if (dimension != INVALID_DIMENSION) {
0655:                        value = aRow[dimension];
0656:                        if (value != null) {
0657:                            node = atDimensionIn(value, dimension, node);
0658:                        } else {
0659:                            return null;
0660:                        }
0661:                    }
0662:                }
0663:                return node;
0664:            }
0665:
0666:            /**
0667:             * Retorna en un array los valores contenidos en aRow para las métricas dadas por metrics
0668:             * @param metrics
0669:             * @param aRow
0670:             * @return
0671:             */
0672:            private Object[] valuesOfFrom(int[] metrics, Object[] aRow) {
0673:                Object[] array = new Object[metricsSize];
0674:                System.arraycopy(aRow, query.getDimensions().length, array, 0,
0675:                        query.getMetrics().length);
0676:                /*array = new Object[metricsSize];
0677:                for (int index = 0; index < metrics.length; index++) {
0678:                if (aRow[metrics[index]]!=null){
0679:                array[index] = (/*(SharedFloat)*//*aRow[metrics[index]]);
0680:                     }else{
0681:                       array[index] = null;
0682:                     }
0683:                   }*/
0684:                return array;
0685:                /*  float[] array;
0686:
0687:                  array = new float[metricsSize];
0688:                    for ( int index = 0; index < metricsSize; index++ )
0689:                    {
0690:                        array[index] = ((Float) aRow[ metrics[index] ]).floatValue();
0691:                    }
0692:
0693:                    return array;*/
0694:            }
0695:
0696:            /**
0697:             * Retorna el porcentaje, con respecto a la fila, de la métrica metric según las
0698:             * coordenadas indicadas por dimensions y values
0699:             * @param metric
0700:             * @param dimensions
0701:             * @param values
0702:             * @return
0703:             */
0704:            public float rowPercentageOf(int metric, int[] dimensions,
0705:                    Object[] values) {
0706:                float total;
0707:
0708:                total = rowTotalOf(metric, dimensions, values);
0709:                return percentageOf(metric, dimensions, values, total);
0710:            }
0711:
0712:            /**
0713:             * Devuelve el total de una métrica en una fila para las dimensiones que se reciben como parámetro
0714:             * @param metric
0715:             * @param dimensions
0716:             * @param values
0717:             * @return
0718:             */
0719:            private float rowTotalOf(int metric, int[] dimensions,
0720:                    Object[] values) {
0721:                return totalOf(metric, dimensions, values, getQuery().getRows());
0722:            }
0723:
0724:            /**
0725:             * Retorna el porcentaje, con respecto a la columna, de la métrica metric según las
0726:             * coordenadas indicadas por dimensions y values
0727:             * @param metric
0728:             * @param dimensions
0729:             * @param values
0730:             * @return
0731:             */
0732:            public float columnPercentageOf(int metric, int[] dimensions,
0733:                    Object[] values) {
0734:                float total;
0735:
0736:                total = columnTotalOf(metric, dimensions, values);
0737:                return percentageOf(metric, dimensions, values, total);
0738:            }
0739:
0740:            /**
0741:             * Devuelve el total de una columna para una métrica para las dimensiones que se reciben como parámetro
0742:             * @param metric
0743:             * @param dimensions
0744:             * @param values
0745:             * @return
0746:             */
0747:            private float columnTotalOf(int metric, int[] dimensions,
0748:                    Object[] values) {
0749:                return totalOf(metric, dimensions, values, getQuery()
0750:                        .getColumns());
0751:            }
0752:
0753:            /**
0754:             * Devuelve el total de una métrica para las dimensiones que se reciben como parámetro
0755:             * @param metric
0756:             * @param dimensions
0757:             * @param values
0758:             * @param rows
0759:             * @return
0760:             */
0761:            private float totalOf(int metric, int[] dimensions,
0762:                    Object[] values, int[] rows) {
0763:                int dimension;
0764:                LinkedList selectedDimensions;
0765:                LinkedList selectedValues;
0766:                boolean found;
0767:                int row;
0768:                int dimensionsLenght;
0769:                int rowsLenght;
0770:
0771:                selectedDimensions = new LinkedList();
0772:                selectedValues = new LinkedList();
0773:                dimensionsLenght = dimensions.length;
0774:                for (int index = 0; index < dimensionsLenght; index++) {
0775:                    dimension = dimensions[index];
0776:                    rowsLenght = rows.length;
0777:                    found = false;
0778:                    for (row = 0; (row < rowsLenght) && !found; row++) {
0779:                        found = (rows[row] == dimension);
0780:                    }
0781:                    if (found) {
0782:                        selectedDimensions.add(new Integer(dimension));
0783:                        selectedValues.add(values[index]);
0784:                    }
0785:                }
0786:
0787:                return measureAtDimensionsValues(metric,
0788:                        toIntArray(selectedDimensions), selectedValues
0789:                                .toArray());
0790:            }
0791:
0792:            /**
0793:             * Devuelve un array de enteros con los índices seleccionados
0794:             * @param selectedIndexes
0795:             * @return
0796:             */
0797:            private int[] toIntArray(LinkedList selectedIndexes) {
0798:                int[] result;
0799:                int index;
0800:                Iterator iterator;
0801:                int selectedDimensionsSize;
0802:
0803:                result = new int[selectedIndexes.size()];
0804:                iterator = selectedIndexes.iterator();
0805:                selectedDimensionsSize = selectedIndexes.size();
0806:                for (index = 0; index < selectedDimensionsSize; index++) {
0807:                    result[index] = ((Integer) iterator.next()).intValue();
0808:                }
0809:                return result;
0810:            }
0811:
0812:            /**
0813:             * Devuelve el porcentaje de una métrica para las dimensiones especificadas
0814:             * @param metric
0815:             * @param dimensions
0816:             * @param values
0817:             * @param total
0818:             * @return
0819:             */
0820:            private float percentageOf(int metric, int[] dimensions,
0821:                    Object[] values, float total) {
0822:                float value;
0823:
0824:                value = measureAtDimensionsValues(metric, dimensions, values);
0825:                if (total == 0) {
0826:                    return 0;
0827:                } else {
0828:                    return value * 100 / total;
0829:                }
0830:            }
0831:
0832:            /**
0833:             * Retorna el valor de la métrica metric según las coordenadas indicadas por
0834:             *  dimensions y values
0835:             * @param metric
0836:             * @param dimensions
0837:             * @param values
0838:             * @return
0839:             */
0840:            public float measureAtDimensionsValues(int metric,
0841:                    int[] dimensions, Object[] values) {
0842:                int metricIndex;
0843:                Object[] measures;
0844:
0845:                for (metricIndex = 0; metricIndex < getMetricsSize(); metricIndex++) {
0846:                    if (getQuery().getMetrics()[metricIndex] == metric) {
0847:                        break;
0848:                    }
0849:                }
0850:                measures = measuresAtDimensionsValues(dimensions, values);
0851:
0852:                return ((SharedFloat) measures[metricIndex + dimensionsSize])
0853:                        .floatValue();
0854:            }
0855:
0856:            /**
0857:             * Retorna los valores de las métricas según las coordenadas indicadas por
0858:             *  dimensions y values
0859:             * @param dimensions
0860:             * @param values
0861:             * @return
0862:             */
0863:            public Object[] measuresAtDimensionsValues(int[] dimensions,
0864:                    Object[] values) {
0865:                Object[] node;
0866:                int index;
0867:                int dimensionsLenght;
0868:
0869:                node = root;
0870:                dimensionsLenght = dimensions.length;
0871:                for (index = 0; index < dimensionsLenght; index++) {
0872:                    node = atDimensionIn(values[index], dimensions[index], node);
0873:                }
0874:                return node;
0875:            }
0876:
0877:            /**
0878:             * Retorna el nodo tipo array a partir del node y el valor value para la
0879:             * dimensión.  Si no existen el nodo tipo diccionario y el tipo
0880:             * array, los crea
0881:             * @param value
0882:             * @param dimension
0883:             * @param node
0884:             * @return
0885:             */
0886:            private Object[] atDimensionIn(Object value, int dimension,
0887:                    Object[] node) {
0888:                HashMap dict;
0889:                Object[] array;
0890:                Object o;
0891:
0892:                if ((node[dimension] instanceof  HashMap)) {
0893:                    dict = (HashMap) node[dimension];
0894:                } else {
0895:                    node[dimension] = dict = new HashMap();
0896:                }
0897:                o = dict.get(value);
0898:                if (o == null) {
0899:                    array = newArray();
0900:                    dict.put(value, array);
0901:                    return array;
0902:                } else {
0903:                    return (Object[]) o;
0904:                }
0905:            }
0906:
0907:            /**
0908:             * Resolución de la serialización
0909:             * @param stream
0910:             * @throws IOException
0911:             */
0912:            public void writeTo(ObjectOutputStream stream) throws IOException {
0913:                stream.writeObject(this );
0914:            }
0915:
0916:            /**
0917:             * Resolución de la des-serialización
0918:             * @param stream
0919:             * @throws IOException
0920:             * @throws ClassNotFoundException
0921:             */
0922:            public void readFrom(ObjectInputStream stream, Pivot pivot)
0923:                    throws IOException, ClassNotFoundException {
0924:                Cube cube;
0925:
0926:                pivot = null;
0927:                cube = (Cube) stream.readObject();
0928:                this .arraySize = cube.arraySize;
0929:                this .definition = cube.definition;
0930:                this .dimensionsCombinations = cube.dimensionsCombinations;
0931:                this .dimensionsSize = cube.dimensionsSize;
0932:                this .metricsSize = cube.metricsSize;
0933:                this .query = cube.query;
0934:                this .queryMetrics = cube.queryMetrics;
0935:                this .root = cube.root;
0936:            }
0937:
0938:            /**
0939:             * Incompleto.  Es para agregar incrementalmente una dimensión
0940:             * @param dimension
0941:             */
0942:            public void addDimension(int dimension) {
0943:                int[] dimensions;
0944:                int factor;
0945:                int[] list;
0946:                int queryDimensionsSize;
0947:                int combination;
0948:                String bits;
0949:                int bitsLenght;
0950:                int pos;
0951:
0952:                dimensions = getQuery().getDimensions();
0953:                queryDimensionsSize = dimensions.length;
0954:                dimensionsCombinations = new LinkedList();
0955:                for (combination = 1; combination <= (Math.pow(2,
0956:                        queryDimensionsSize) - 1); combination++) {
0957:                    bits = Integer.toBinaryString(combination);
0958:                    bitsLenght = bits.length();
0959:                    list = new int[bitsLenght];
0960:                    for (int index = 0; index < bitsLenght; index++) {
0961:                        pos = bitsLenght - 1 - index;
0962:                        factor = Character.digit(bits.charAt(pos), 10);
0963:                        if (factor == 1) {
0964:                            list[index] = factor * dimensions[index];
0965:                        } else {
0966:                            list[index] = INVALID_DIMENSION;
0967:                        }
0968:                    }
0969:                    dimensionsCombinations.add(list);
0970:                }
0971:            }
0972:
0973:            /**
0974:             * Incompleto.  Es para agregar incrementalmente dimensiones
0975:             * @param newDimensions
0976:             */
0977:            public void fillWithNewDimensions(LinkedList newDimensions) {
0978:                /*    Iterator iterator;
0979:                    LinkedList newDimensionsCombinations;
0980:                    int dimension;
0981:
0982:                    iterator = newDimensions.iterator();
0983:                    while (iterator.hasNext()) {
0984:                        dimension = ((Integer) iterator.next()).intValue();
0985:
0986:
0987:                    }*/
0988:                ///todo: Falta completar resolucón incremental de dimensiones
0989:            }
0990:
0991:            /**
0992:             * Devuelve un iterador para recorrer los contenidos de la estructura Cube
0993:             * @return
0994:             */
0995:            public CubeIterator iterator() {
0996:                return CubeIterator.on(this );
0997:            }
0998:
0999:            /**
1000:             * Devuelve un iterador para recorrer los valores de una dimensión aplicando el criterio de ordenamiento
1001:             * @param table
1002:             * @param dimensionIndex
1003:             * @return
1004:             */
1005:            public Iterator sortedIteratorFor(HashMap table, int dimensionIndex) {
1006:                /*if ( getQuery().getDimensionRank()[dimensionIndex] > 0 ){
1007:                  TreeMap treeMap = new TreeMap(getQuery().entryComparatorFor(dimensionIndex));
1008:                  treeMap.entrySet().addAll(table.entrySet());
1009:                  return treeMap.entrySet().iterator();
1010:                }
1011:                else{*/
1012:                TreeSet set;
1013:                set = new TreeSet(getQuery().entryComparatorFor(
1014:                        getQuery().getDimensions()[dimensionIndex]));
1015:                if (table != null) {
1016:                    set.addAll(table.entrySet());
1017:                }
1018:                return set.iterator();
1019:                //}
1020:            }
1021:
1022:            /**
1023:             * Retorna un iterador ordenado sobre los valores para una dimensión a partir de las coordenadas indicadas por
1024:             * previousDimensions y values
1025:             * @param dimension
1026:             * @param previousDimensions
1027:             * @param values
1028:             * @return
1029:             */
1030:            public Iterator valuesFor(int dimension, int[] previousDimensions,
1031:                    Object[] values) {
1032:                Object[] node;
1033:                HashMap table;
1034:
1035:                node = measuresAtDimensionsValues(previousDimensions, values);
1036:                table = (HashMap) node[dimension];
1037:
1038:                return sortedIteratorFor(table, dimension);
1039:            }
1040:
1041:            public Set[] getDimensionValues() {
1042:                if (dimensionValues == null) {
1043:                    dimensionValues = new TreeSet[getFullDimensionsSize()];
1044:                    for (int i = 0; i < dimensionValues.length; i++) {
1045:                        dimensionValues[i] = new TreeSet();
1046:                    }
1047:                }
1048:                return dimensionValues;
1049:            }
1050:
1051:            private int getFullDimensionsSize() {
1052:                return fullDimensionsSize;
1053:            }
1054:
1055:            /**
1056:             * Realiza las operaciones posteriores a la carga de datos del cubo. Se calculan los average y los maximos
1057:             * y minimos para totales.
1058:             */
1059:            public void afterFill() {
1060:                if (emptyCube()) {
1061:                    root = newEmptyRoot();
1062:                } else {
1063:                    int[] indexes = getAverageMetricsIndexes();
1064:                    if (indexes.length > 0) {
1065:                        setAverageValues(root, indexes);
1066:                    }
1067:                    indexes = getMinMaxFootersIndexes();
1068:                    if (indexes.length > 0) {
1069:                        int[] dimensionsOrder = getDimensionByGroupingOrder();
1070:                        for (int i = 0; i < dimensionsOrder.length; i++) {
1071:                            setMinMaxFooters(root, indexes, dimensionsOrder, 0);
1072:                        }
1073:                    }
1074:                    indexes = getCountDistinctIndexes();
1075:                    if (indexes.length > 0) {
1076:                        setNodesCountValues(root, indexes);
1077:                    }
1078:                }
1079:                if (!excludedNodes.isEmpty()) {
1080:                    Iterator it = dimensionsCombinations.iterator();
1081:                    while (it.hasNext()) {
1082:                        int[] dimensions = (int[]) it.next();
1083:                        if (excludedNodes.containsKey(dimensions))
1084:                            addExcludedNode((Object[]) excludedNodes
1085:                                    .get(dimensions), dimensions);
1086:                    }
1087:                }
1088:                System.gc();
1089:                System.gc();
1090:                System.gc();
1091:                System.gc();
1092:                System.gc();
1093:            }
1094:
1095:            private int[] getCountDistinctIndexes() {
1096:                Object[] metrics = definition.getMetrics();
1097:                LinkedList indexes = new LinkedList();
1098:                for (int i = 0; i < metrics.length; i++) {
1099:                    Object metric = metrics[i];
1100:                    if (metric instanceof  ReportMetricSpec) {
1101:                        ReportMetricSpec metricSpec = (ReportMetricSpec) metric;
1102:                        if ((metricSpec.getAggregateType().getType() == CalculationType.COUNT_DISTINCT_TYPE && isMetricInQuery(i))) {
1103:                            indexes.add(new Integer(i + dimensionsSize));
1104:                        }
1105:                    }
1106:                }
1107:                return toIntArray(indexes);
1108:            }
1109:
1110:            private void setNodesCountValues(Object[] node, int[] indexes) {
1111:                for (int j = 0; j < indexes.length; j++) {
1112:                    int index = indexes[j];
1113:                    node[index] = SharedFloat.newFrom(((Set) node[index])
1114:                            .size());
1115:                }
1116:                for (int i = 0; i < dimensionsSize; i++) {
1117:                    if (node[i] != null) {
1118:                        Map nodes = (Map) node[i];
1119:                        Iterator it = nodes.values().iterator();
1120:                        while (it.hasNext()) {
1121:                            Object[] childNode = (Object[]) it.next();
1122:                            setNodesCountValues(childNode, indexes);
1123:                        }
1124:                    }
1125:                }
1126:            }
1127:
1128:            /**
1129:             * Obtiene las dimensiones existentes en la query en el orden en el que agrupan. Es decir, desde el primer nivel
1130:             * de row en el que se agrupan todas las dimensiones, hacia las columnas que despliegan las metricas.
1131:             * @return
1132:             */
1133:            private int[] getDimensionByGroupingOrder() {
1134:                int[] result = new int[query.getDimensions().length];
1135:                int[] queryColumns = query.getColumns();
1136:                int[] queryRows = query.getRows();
1137:                System.arraycopy(queryRows, 0, result, 0, queryRows.length);
1138:                System.arraycopy(queryColumns, 0, result, queryRows.length,
1139:                        queryColumns.length);
1140:                return result;
1141:            }
1142:
1143:            /**
1144:             * Retorna un nuevo nodo root vacío (solo con valores de metricas inicializados a 0).
1145:             * @return
1146:             */
1147:            private Object[] newEmptyRoot() {
1148:                Object[] result = new Object[arraySize];
1149:                for (int i = dimensionsSize; i < result.length; i++) {
1150:                    result[i] = SharedFloat.newFrom(0);
1151:                }
1152:                return result;
1153:            }
1154:
1155:            /**
1156:             * Retorna si el cubo que se lleno esta vacio o no.
1157:             * @return true si el cubo esta vacio.
1158:             */
1159:            private boolean emptyCube() {
1160:                int i = 0;
1161:                for (; i < dimensionsSize; i++) {
1162:                    if (root[i] != null) {
1163:                        return false;
1164:                    }
1165:                }
1166:                for (; i < arraySize; i++) {
1167:                    if (!Float.isNaN(((SharedFloat) root[i]).floatValue())) {
1168:                        return false;
1169:                    }
1170:                }
1171:                return true;
1172:            }
1173:
1174:            /**
1175:             * Una vez que esta hecho el cubo, recorre los nodos para buscar los maximos y minimos para los footers de grupo
1176:             * que tengan seleccionada alguna de estas funciones. Para ello recorre recursivamente el cubo desde el ultimo
1177:             * nivel hacia el que mas agrupa, seteando en cada nivel los maximos y minimos correspondientes.
1178:             * @param node el nodo que se esta recorriendo
1179:             * @param indexes los indices de las metricas que tienen funciones max y min en sus footers
1180:             * @param modelIndexes los indices del modelo, desde el que mas agrupa hasta el ultimo.
1181:             * @param modelIndex indice dentro del que estoy ubicado dentro de modelIndexes (se referenciara el indice
1182:             * de la dimension como modelIndexes[modelIndex])
1183:             */
1184:            private void setMinMaxFooters(Object[] node, int[] indexes,
1185:                    int[] modelIndexes, int modelIndex) {
1186:                if (modelIndex + 1 < modelIndexes.length) {
1187:                    Iterator iterator = ((Map) node[modelIndexes[modelIndex]])
1188:                            .entrySet().iterator();
1189:                    while (iterator.hasNext()) {
1190:                        Object[] childNode = (Object[]) ((Map.Entry) iterator
1191:                                .next()).getValue();
1192:                        for (int childIndex = modelIndex + 1; childIndex < modelIndexes.length; childIndex++) {
1193:                            setMinMaxFooters(childNode, indexes, modelIndexes,
1194:                                    childIndex);
1195:                        }
1196:                    }
1197:                }
1198:                int nodeIndex = getNodeIndex(node, modelIndexes);
1199:                if (nodeIndex == modelIndexes[modelIndex]) {
1200:                    for (int i = 0; i < indexes.length; i++) {
1201:                        int index = indexes[i];
1202:                        node[index] = obtainValue(
1203:                                (Map) node[modelIndexes[modelIndex]], index);
1204:                    }
1205:                }
1206:                //Si el indice de la dimension esta en Rows y si hay columns, setea los valores maximos y minimos para columns
1207:                if (isInRows(nodeIndex) && getQuery().getColumns() != null
1208:                        && getQuery().getColumns().length > 0) {
1209:                    setColumnValues(node, indexes, modelIndexes, nodeIndex);
1210:                }
1211:            }
1212:
1213:            /**
1214:             * Dado un indice, retorna si ese indice se encuentra dentro de las row de la query.
1215:             * @param dimensionIndex indice de la dimension que se busca
1216:             * @return true si la dimension esta dentro de rows de la query
1217:             */
1218:            private boolean isInRows(int dimensionIndex) {
1219:                for (int i = 0; i < getQuery().getRows().length; i++) {
1220:                    int row = getQuery().getRows()[i];
1221:                    if (dimensionIndex == row) {
1222:                        return true;
1223:                    }
1224:                }
1225:                return false;
1226:            }
1227:
1228:            /**
1229:             * Setea los totales maximos y minimos para las columnas. Dentro del cube, un nodo que esta en row tiene el
1230:             * despliegue de valores de las distintas combinaciones de column, para totales. Por ello, se deben recorrer
1231:             * todos estos nodos, y buscar los valores maximos y minimos (se hace cuando se termino de cargar el cubo).
1232:             * @param node nodo al que se le setean los maximos y minimos
1233:             * @param indexes indices de las metricas que tienen maximos y minimos en footer.
1234:             * @param modelIndexes indices de las dimensiones en el orden del modelo de la query
1235:             * @param maxNodeIndex si el nodo agrupa en el nivel n, se pasara el indice de la dimension n+1 en orden de agrupacion
1236:             */
1237:            private void setColumnValues(Object[] node, int[] indexes,
1238:                    int[] modelIndexes, int maxNodeIndex) {
1239:                for (int j = getQuery().getRows().length; j < modelIndexes.length; j++) {
1240:                    int modelIndex = modelIndexes[j];
1241:                    Iterator iterator = ((Map) node[modelIndex]).entrySet()
1242:                            .iterator();
1243:                    while (iterator.hasNext()) {
1244:                        Map.Entry entry = (Map.Entry) iterator.next();
1245:                        Vector dimensionsValues = new Vector();
1246:                        dimensionsValues.add(entry.getKey());
1247:                        Object[] childNode = (Object[]) entry.getValue();
1248:                        for (int i = 0; i < indexes.length; i++) {
1249:                            int footerIndex = indexes[i];
1250:                            childNode[footerIndex] = getFooterValueFor(node,
1251:                                    dimensionsValues, footerIndex,
1252:                                    modelIndexes, j, maxNodeIndex);
1253:                        }
1254:                        setSubColumnValues(node, childNode, dimensionsValues,
1255:                                indexes, modelIndexes, j + 1, maxNodeIndex, j);
1256:                    }
1257:                }
1258:            }
1259:
1260:            /**
1261:             * Setea los valores de todos los nodos intermedios en column recorriendolos recursivamente desde el que mas agrupa
1262:             * al que menos (el que despliega en las metricas).
1263:             * @param node nodo de Row al que se le estan seteando los valores
1264:             * @param childNode nodo superior del modelo de column
1265:             * @param dimensionsValues valores agrupados de los nodos anteriores de column
1266:             * @param indexes indices de las metricas que usan max y min en totales
1267:             * @param modelIndexes indices de las dimensiones en el orden del modelo de la query
1268:             * @param columnIndex indice de la columna dentro de los model indexes (se referenciara modelIndexes[columnIndex]).
1269:             * @param maxNodeIndex si el nodo agrupa en el nivel n, se pasara el indice de la dimension n+1 en orden de agrupacion
1270:             * @param startingColumnIndex primera dimension en column que se esta considerando en la agrupacion de dimensionsValues
1271:             */
1272:            private void setSubColumnValues(Object[] node, Object[] childNode,
1273:                    Vector dimensionsValues, int[] indexes, int[] modelIndexes,
1274:                    int columnIndex, int maxNodeIndex, int startingColumnIndex) {
1275:                if (columnIndex < modelIndexes.length) {
1276:                    Iterator iterator = ((Map) childNode[modelIndexes[columnIndex]])
1277:                            .entrySet().iterator();
1278:                    while (iterator.hasNext()) {
1279:                        Map.Entry entry = (Map.Entry) iterator.next();
1280:                        Vector clonedValues = (Vector) dimensionsValues.clone();
1281:                        clonedValues.add(entry.getKey());
1282:                        Object[] subNode = (Object[]) entry.getValue();
1283:                        for (int i = 0; i < indexes.length; i++) {
1284:                            int footerIndex = indexes[i];
1285:                            subNode[footerIndex] = getFooterValueFor(node,
1286:                                    clonedValues, footerIndex, modelIndexes,
1287:                                    startingColumnIndex, maxNodeIndex);
1288:                        }
1289:                        setSubColumnValues(node, subNode, clonedValues,
1290:                                indexes, modelIndexes, columnIndex + 1,
1291:                                maxNodeIndex, startingColumnIndex);
1292:                    }
1293:                }
1294:            }
1295:
1296:            /**
1297:             * Obtiene el valor maximo o minimo para una column segun los valores de su nodo row
1298:             * @param node nodo row que se esta considerando
1299:             * @param dimensionValues valores agrupados de las dimensiones en column. Se agrupan los valores desde el nivel inicial
1300:             * @param index indice de la metrica
1301:             * @param modelIndexes indices de las dimensiones en el orden del modelo de la query
1302:             * @param columnIndex indice de la columna dentro de los model indexes (se referenciara modelIndexes[columnIndex]).
1303:             * @param maxNodeIndex si el nodo agrupa en el nivel n, se pasara el indice de la dimension n+1 en orden de agrupacion
1304:             * @return
1305:             */
1306:            private SharedFloat getFooterValueFor(Object[] node,
1307:                    Vector dimensionValues, int index, int[] modelIndexes,
1308:                    int columnIndex, int maxNodeIndex) {
1309:                SharedFloat result = SharedFloat.newFrom(Float.NaN);
1310:                Iterator iterator = ((Map) node[maxNodeIndex]).entrySet()
1311:                        .iterator();
1312:                while (iterator.hasNext()) {
1313:                    Map.Entry entry = (Map.Entry) iterator.next();
1314:                    Map subNodeValues = (Map) ((Object[]) entry.getValue())[modelIndexes[columnIndex]];
1315:                    Object[] columnNode = (Object[]) subNodeValues
1316:                            .get(dimensionValues.elementAt(0));
1317:                    for (int i = 1; i < dimensionValues.size()
1318:                            && columnNode != null
1319:                            && columnIndex + i < modelIndexes.length; i++) {
1320:                        Object dimensionValue = dimensionValues.elementAt(i);
1321:                        subNodeValues = (Map) columnNode[modelIndexes[columnIndex
1322:                                + i]];
1323:                        columnNode = (Object[]) subNodeValues
1324:                                .get(dimensionValue);
1325:                    }
1326:                    if (columnNode != null) {
1327:                        result = (SharedFloat) groupFooterStrategies[index
1328:                                - dimensionsSize].operate(columnNode, index,
1329:                                result, null);
1330:                    } else {
1331:                        //Simula pasarle un nodo por parametro. Dentro del stategy accede a object[0], y le dara el mismo valor.
1332:                        result = (SharedFloat) groupFooterStrategies[index
1333:                                - dimensionsSize].operate(
1334:                                new Object[] { result }, 0, null, null);
1335:                    }
1336:                }
1337:                return result;
1338:            }
1339:
1340:            /**
1341:             * Obtiene el mayor indice de grupo de un nodo. Por ejemplo si un estamos considerando un nodo que tiene los valores
1342:             * de la primer dimension en row, retornará el indice de la segunda dimension en row.
1343:             * @param node
1344:             * @param modelIndexes
1345:             * @return
1346:             */
1347:            private int getNodeIndex(Object[] node, int[] modelIndexes) {
1348:                for (int i = 0; i < modelIndexes.length; i++) {
1349:                    int modelIndex = modelIndexes[i];
1350:                    if (node[modelIndex] != null) {
1351:                        return modelIndex;
1352:                    }
1353:                }
1354:                return modelIndexes[0];
1355:            }
1356:
1357:            /**
1358:             * Dado un conjunto de nodos y un indice de metrica, retorna el valor para el total de esa metrica, calculando,
1359:             * segun corresponda a la metrica, maximo o minimo.
1360:             * @param nodeValues subnodos
1361:             * @param index indice de la metrica
1362:             * @return
1363:             */
1364:            private SharedFloat obtainValue(Map nodeValues, int index) {
1365:                SharedFloat result = SharedFloat.newFrom(Float.NaN);
1366:                for (Iterator iterator = nodeValues.entrySet().iterator(); iterator
1367:                        .hasNext();) {
1368:                    Object[] node = (Object[]) ((Map.Entry) iterator.next())
1369:                            .getValue();
1370:                    result = (SharedFloat) groupFooterStrategies[index
1371:                            - dimensionsSize]
1372:                            .operate(node, index, result, null);
1373:                }
1374:                return result;
1375:            }
1376:
1377:            /**
1378:             * Busca los indices de las metricas que utilizan maximos y minimos
1379:             * @return
1380:             */
1381:            private int[] getMinMaxFootersIndexes() {
1382:                Object[] metrics = definition.getMetrics();
1383:                LinkedList indexes = new LinkedList();
1384:                for (int i = 0; i < metrics.length; i++) {
1385:                    Object metric = metrics[i];
1386:                    if (metric instanceof  ReportMetricSpec) {
1387:                        ReportMetricSpec metricSpec = (ReportMetricSpec) metric;
1388:                        if ((metricSpec.getGroupFooterType().getType() == CalculationType.MAX_TYPE || metricSpec
1389:                                .getGroupFooterType().getType() == CalculationType.MIN_TYPE)
1390:                                && isMetricInQuery(i)) {
1391:                            indexes.add(new Integer(i + dimensionsSize));
1392:                        }
1393:                    }
1394:                }
1395:                return toIntArray(indexes);
1396:            }
1397:
1398:            /**
1399:             * Setea los valores promedio para un nodo
1400:             * @param node nodo a setear
1401:             * @param indexes indices de las metricas que utilizan average
1402:             */
1403:            private void setAverageValues(Object[] node, int[] indexes) {
1404:                setAverageValueForNode(node, indexes);
1405:                int[] dimensions = query.getDimensions();
1406:                for (int i = 0; i < dimensions.length; i++) {
1407:                    int dimension = dimensions[i];
1408:                    if (node[dimension] != null) {
1409:                        setAverageValueForChild((Map) node[dimension], indexes);
1410:                    }
1411:                }
1412:            }
1413:
1414:            /**
1415:             * Recorre los subnodos de un nodo, seteando recursivamente los valores promedio
1416:             * @param map subnodos
1417:             * @param indexes
1418:             */
1419:            private void setAverageValueForChild(Map map, int[] indexes) {
1420:                for (Iterator iterator = map.entrySet().iterator(); iterator
1421:                        .hasNext();) {
1422:                    Map.Entry entry = (Map.Entry) iterator.next();
1423:                    setAverageValues((Object[]) entry.getValue(), indexes);
1424:                }
1425:            }
1426:
1427:            /**
1428:             * Setea especificamente el valor del average de un nodo, diferenciando si este es o no total.
1429:             * @param node
1430:             * @param indexes
1431:             */
1432:            private void setAverageValueForNode(Object[] node, int[] indexes) {
1433:                for (int i = 0; i < indexes.length; i++) {
1434:                    int index = indexes[i];
1435:                    if (isLastLevel(node)
1436:                            && metricStrategies[index] instanceof  AverageStrategy) {
1437:                        node[index + dimensionsSize] = ((SharedFloat) node[index
1438:                                + dimensionsSize]).div((SharedFloat) node[index
1439:                                + dimensionsSize + metricsSize / 2]);
1440:                    } else if (groupFooterStrategies[index] instanceof  AverageStrategy) {
1441:                        node[index + dimensionsSize] = ((SharedFloat) node[index
1442:                                + dimensionsSize]).div((SharedFloat) node[index
1443:                                + dimensionsSize + metricsSize / 2]);
1444:                    }
1445:                }
1446:            }
1447:
1448:            /**
1449:             * Retorna verdadero si el nodo corresponde al ultimo nivel de grupo, es decir, no agrupa a otras dimensiones.
1450:             * @param node
1451:             * @return
1452:             */
1453:            private boolean isLastLevel(Object[] node) {
1454:                for (int i = 0; i < node.length && i < getDimensionsSize(); i++) {
1455:                    if (node[i] != null) {
1456:                        return false;
1457:                    }
1458:                }
1459:                return true;
1460:            }
1461:
1462:            public Object[] getMetricsValuesAt(int[] dimensions, Object[] values) {
1463:                Object[] node;
1464:                int index;
1465:                int dimensionsLenght;
1466:
1467:                node = root;
1468:                dimensionsLenght = dimensions.length;
1469:                for (index = 0; index < dimensionsLenght; index++) {
1470:                    node = atDimensionValueFor(values[index],
1471:                            dimensions[index], node);
1472:                }
1473:                return node;
1474:            }
1475:
1476:            private Object[] atDimensionValueFor(Object value, int dimension,
1477:                    Object[] node) {
1478:                HashMap dict;
1479:                Object o;
1480:                if (node == null) {
1481:                    return node;
1482:                }
1483:                if ((node[dimension] instanceof  HashMap)) {
1484:                    dict = (HashMap) node[dimension];
1485:                } else {
1486:                    return null;
1487:                }
1488:                o = dict.get(value);
1489:                return (Object[]) o;
1490:            }
1491:
1492:            public Set getDimensionValues(int index) throws InfoException {
1493:                if (getDimensionValues()[index].isEmpty() && pivot != null) {
1494:                    getDimensionValues()[index].addAll(pivot
1495:                            .getDimensionValues(index));
1496:                }
1497:                return getDimensionValues()[index];
1498:            }
1499:
1500:            protected void setPivot(Pivot pivot) {
1501:                this .pivot = pivot;
1502:            }
1503:
1504:            public MetricCalculationStrategy[] getMetricStrategies() {
1505:                return metricStrategies;
1506:            }
1507:
1508:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.