001: package com.calipso.reportgenerator.reportcalculator;
002:
003: import java.util.*;
004:
005: /**
006: * Itera un Cube.
007: * A diferencia de un iterador tradicional de Java, la interface separa la
008: * obtención del valor actual (current) del avance (advance).
009: * Además, luego de cada advance, se recalculan los totales por los que pueda
010: * haberse atravesado. Es decir, se genera una lista de totales para cada corte
011: * de control realizado por los últimos valores con respecto a los anteriores.
012: * La forma de operar es obtener el iterador del Cube.
013: * Hacer recursivamente advance y consultar por current y por currentTotals
014: * Luego de obtener el último current hasNext va a devolver false.
015: * En ese momento debe hacerse un nuevo advance para obtener los últimos totales.
016: */
017: public class CubeIterator {
018: LinkedList totals;
019: Iterator[] iterators;
020: Map.Entry[] entries;
021: int[] dimensions;
022: int[] metrics;
023: Cube cube;
024: int metricsSize;
025: int dimensionsSize;
026:
027: /**
028: * Crea un iterador para recorrer el Cube que se recibe como parámetro
029: * @param cube
030: * @return
031: */
032: public static CubeIterator on(Cube cube) {
033: return new CubeIterator(cube);
034: }
035:
036: /**
037: * Crea un iterador para recorrer el Cube que se recibe como parámetro
038: * @param cube
039: */
040: public CubeIterator(Cube cube) {
041: this .cube = cube;
042: initialize();
043: }
044:
045: /**
046: * Inicializa los valores que describen al cubo, cantidad de métricas, cantidad de dimensiones y prepara las
047: * estructuras internas
048: */
049: private void initialize() {
050: dimensionsSize = cube.getDimensionsSize();
051: metricsSize = cube.getMetricsSize();
052: dimensions = cube.getIntegerArray(0, dimensionsSize);
053: metrics = cube.getIntegerArray(dimensionsSize, metricsSize);
054: entries = new Map.Entry[dimensionsSize + 1];
055: iterators = new Iterator[dimensionsSize];
056: // Carga el primer nodo
057: entryAtPut(-1, new Entry("root", cube.getRoot()));
058: }
059:
060: /**
061: * Devuelve una asociación que representa a un nodo del cube
062: * @param index
063: * @return
064: */
065: public Map.Entry entryAt(int index) {
066: return entries[index + 1];
067: }
068:
069: /**
070: * Agrega una asociación que representa a un nodo del cube, reemplazando el valor de la dimensión por el valor real
071: * @param index
072: * @param value
073: */
074: public void entryAtPut(int index, Map.Entry value) {
075: entries[index + 1] = value;
076: fillEntryWithParentsValues(index);
077: }
078:
079: /**
080: * Asigna a un nodo tipo array indicado por index los
081: * valores de las métricas bajo las cuales está, es decir,
082: * de los nodos encontrados ascendentemente. Esto se usa
083: * para cuando se devuelven estos nodos a modo de totales
084: * @param index
085: */
086: private void fillEntryWithParentsValues(int index) {
087: int i;
088: Map.Entry entry;
089: for (i = 0; i <= index; i++) {
090: entry = entries[i + 1];
091: if (entry != null) {
092: ((Object[]) entries[index + 1].getValue())[dimensions[i]] = entry
093: .getKey();
094: }
095: }
096: }
097:
098: /**
099: * Inicializa un nuevo iterador para una dimensión
100: * @param dimensionIndex
101: */
102: public void initializeIterator(int dimensionIndex) {
103: iterators[dimensionIndex] = iteratorFor(dimensionIndex);
104: if (iterators[dimensionIndex].hasNext()) {
105: updateEntry(iterators[dimensionIndex], dimensionIndex);
106: }
107: }
108:
109: /**
110: * Crea un iterador para una dimensión
111: * @param dimensionIndex
112: * @return
113: */
114: private Iterator iteratorFor(int dimensionIndex) {
115: HashMap table;
116: Object[] node;
117:
118: node = (Object[]) entryAt(dimensionIndex - 1).getValue();
119: int cubeDimIndex = dimensions[dimensionIndex];
120: table = (HashMap) node[cubeDimIndex];
121: if (table != null) {
122: return cube.sortedIteratorFor(table, cubeDimIndex);
123: } else {
124: return null;
125: }
126: }
127:
128: /**
129: * Agrega una asociación que representa a un nodo para el valor de una dimensión
130: * @param iterator
131: * @param dimensionIndex
132: */
133: private void updateEntry(Iterator iterator, int dimensionIndex) {
134: entryAtPut(dimensionIndex, (Map.Entry) iterator.next());
135: }
136:
137: /**
138: * El iterador avanza una posicion.
139: */
140: public void advance() {
141: totals = new LinkedList();
142: advanceAt(dimensionsSize - 1);
143: }
144:
145: /**
146: * Avance el iterador para la dimensión dimensionIndex, si es
147: * que está asignado, si no, o si se terminó, hace avanzar el
148: * iterador anterior y luego crea uno nuevo
149: * @param dimensionIndex
150: */
151: private void advanceAt(int dimensionIndex) {
152:
153: if (dimensionIndex >= 0) {
154: if (iterators[dimensionIndex] != null) {
155: if (iterators[dimensionIndex].hasNext()) {
156: updateEntry(iterators[dimensionIndex],
157: dimensionIndex);
158: return;
159: } else {
160: iterators[dimensionIndex] = null;
161: totals.add(totalFrom(dimensionIndex - 1));
162: }
163: }
164: advanceAt(dimensionIndex - 1);
165: initializeIterator(dimensionIndex);
166: }
167: }
168:
169: /**
170: * Crea un array con los totales para la dimensión dimensionIndex
171: * Si el total a devolver es el raíz, puede tener hashmaps que hay
172: * que sacar. Si no fuese por esto devolveríamos directamente:
173: * entryAt(dimensionIndex).getValue();
174: * @param dimensionIndex
175: * @return Object
176: */
177: private Object totalFrom(int dimensionIndex) {
178: Object[] result;
179: int i;
180: Object[] node;
181:
182: node = (Object[]) entryAt(dimensionIndex).getValue();
183: result = new Object[node.length];
184: for (i = 0; i < dimensionsSize; i++) {
185: if (node[i] instanceof HashMap) {
186: result[i] = null;
187: } else {
188: result[i] = node[i];
189: }
190: }
191: for (i = 0; i < metricsSize; i++) {
192: result[dimensionsSize + i] = node[metrics[i]];
193: }
194: return result;
195: }
196:
197: /**
198: * A partir de la posición adonde apunta el CubeIterator
199: * devuelve verdadero/falso dependiendo si hay o no mas datos en el cubo
200: */
201: public boolean hasNext() {
202: Iterator iterator;
203: if (iterators.length == 0) {
204: return false;
205: }
206: if (iterators[0] == null) {
207: iterator = iteratorFor(0);
208: if (iterator != null) {
209: return iterator.hasNext();
210: } else {
211: return false;
212: }
213: }
214: for (int i = 0; i < dimensionsSize; i++) {
215: iterator = iterators[i];
216: if (iterator.hasNext()) {
217: return true;
218: }
219: }
220: return false;
221: }
222:
223: /**
224: * Crea un array con los valores actuales
225: * @return [] Object
226: */
227: public Object[] current() {
228: Object[] result;
229: int i;
230: Object[] node;
231:
232: result = new Object[dimensionsSize + metricsSize];
233: for (i = 0; i < dimensionsSize; i++) {
234: result[i] = entryAt(i).getKey();
235: }
236: node = (Object[]) entryAt(dimensionsSize - 1).getValue();
237: for (i = 0; i < metricsSize; i++) {
238: result[dimensionsSize + i] = node[metrics[i]];
239: }
240: return result;
241: }
242:
243: /**
244: * Retorna una lista enlazada con los totales actuales
245: * @return LinkedList
246: */
247: public Collection currentTotals() {
248: return totals;
249: }
250: }
|