JPivot, como crear otro UI para especificar las dimensiones del cubo OLAP
El interface gráfico que proporciona JPivot para configurar los cubos OLAP, en algunos casos puede dejar mucho de desear ya que desde mi punto de vista, no es muy usable de cara al usuario y se puede llegar a perder en el intento.
Por este motivo, investigé en Internet sobre otras alternativas, pero no, todo el mundo se quejaba en los foros, pero nadie daba soluciones o pistas de como obtenerlo.
Como no descubrí nada decente, me bajé el código fuente de JPivot y sus librerías asociadas e investigé el modo en como interactua el UI con el modelo, dando como resultado lo que expongo en este tutorial.
Tras largas horas de investigación, vi que lo que deseaba modificar era pintado mediante transformaciones XSLT a datos XML… asi que sólo tenía que obtener el XML y crear la plantilla que lo pintase de acuerdo a mis necesidades.
Interface gráfico original y el alternativo (más usable) que se pretende conseguir:
UI original (inicial): |
UI deseado (final): |
---|---|
![]() |
![]() |
Plantilla XSLT para obtener el XML que representa el modelo de JPivot en cada instante:
Mirando las JSP, Java, etc.. me di cuenta de que el cuadro que deseo modificar se pinta en la plantilla hierarchy-navigator.xsl
, por lo que cambié su contenido por
la plantilla XSLT identidad, que simplemente muestra el XML tal cual le viene sin modificarlo.
La plantilla XSLT es la siguiente:
1 2 3 4 5 6 7 8 9 10 |
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml"/> <xsl:template match="/"> <xsl:apply-templates/> </xsl:template> <xsl:template match="*|@*|node()"> <xsl:value-of select="."/> </xsl:template> </xsl:stylesheet> |
Datos XML obtenidos al aplicar la plantilla XSLT anterior presentando el modelo de JPivot en un instante:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
<?xml version="1.0" encoding="UTF-8"?> <cat-edit accept-id="navi01.hiernav.accept" accept-title="Aceptar" cancel-id="navi01.hiernav.cancel" cancel-title="Cancelar" ok-id="navi01.hiernav.ok" ok-title="Aplicar" revert-id="navi01.hiernav.revert" revert-title="Deshacer"> <cat-category icon="column.png" name="Columnas"> <cat-item id="wcf246d3cf1" name="Measures"> <cat-button icon="row.png" id="wcff81970b0"></cat-button> <cat-button icon="filter.png" id="wcf42592523"></cat-button> <move-button></move-button> <move-button id="wcf37e33072" style="fwd" title="Mover"></move-button> </cat-item> <cat-item id="wcf6bb8545" name="Material"> <cat-button icon="row.png" id="wcf2781bcd8"></cat-button> <cat-button icon="filter.png" id="wcfec0b7b75"></cat-button> <move-button id="wcf88376955" style="bwd" title="Mover"></move-button> <move-button id="wcf6229a1f0" style="fwd" title="Mover"></move-button> </cat-item> <cat-item id="wcfdcfd87d9" name="Products"> <cat-button icon="row.png" id="wcfae4ed348"></cat-button> <cat-button icon="filter.png" id="wcfda24e363"></cat-button> <move-button id="wcf4ddd5a47" style="bwd" title="Mover"></move-button> <move-button></move-button> </cat-item> </cat-category> <cat-category icon="row.png" name="Filas"> <cat-item id="wcff88fe80a" name="Region"> <cat-button icon="column.png" id="wcfab820d63"></cat-button> <cat-button icon="filter.png" id="wcfd13e1660"></cat-button> <move-button id="wcf27996280" style="fwd" title="Mover"></move-button> </cat-item> <cat-item id="wcf1c69bcd6" name="Time"> <cat-button icon="column.png" id="wcf5ceacf5d"></cat-button> <cat-button icon="filter.png" id="wcf1bc38a62"></cat-button> <move-button id="wcf5d3e4a37" style="bwd" title="Mover"></move-button> </cat-item> </cat-category> <cat-category icon="filter.png" name="Filtro"> <cat-item id="wcfce7ae485" name="Advertising"> <slicer-value label="Media[3]" level="Media"></slicer-value> <cat-button icon="column.png" id="wcfab150756"></cat-button> <cat-button icon="row.png" id="wcfe5a8875d"></cat-button> </cat-item> </cat-category> </cat-edit> |
Plantilla XSLT que genere el interface gráfico deseado (hierarchy-navigator.xsl
):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="cat-edit"> <script type="text/javascript"> <xsl:comment> /** * Dada una cadena de tokens separados por "_" y una posición (comenzando por 0), devuelve el token que ocupa esa posición. * Por ejemplo, para cadena: aaaaaaa_bbbbb y pos: 0 * La función devolvería: aaaaaaa */ function extrae(cadena, pos){ lista = cadena.split("_"); return lista[pos]; } /** * Envia el formulario con un determinado código de acción * @param listId: Identificador de la lista. */ function sendToAux(listId, indice){ var lista = document.getElementById(listId); var index = lista.selectedIndex; if (index == -1){ alert("Debe seleccionar el elemento sobre el cuál desea realizar la operación"); } else { var value = lista.options[index].value; var name = extrae(value, indice); var operationField = document.getElementById('operation'); // Introducimos el name en un input hidden y enviamos el formulario operationField.name = name; operationField.value = name; document.forms[0].submit(); } } </xsl:comment> </script> <div id="{$renderId}" style="display:block;"> <!-- Este control será el que contenta el código de la operación. Lo importante es el name y no el value --> <input type="hidden" id="operation" name="" value=""/> <table style="border-style:solid;border-width:1px;border-color:#dadada"> <colgroup> <col width="300px"/> <col width="350px"/> <col width="350px"/> </colgroup> <thead> <tr style="background-color:#eeeeff;"> <th>FILTROS</th> <th>FILAS</th> <th>COLUMNAS</th> </tr> </thead> <tfoot> <tr> <td colspan="3"> <hr/> <div id="navigatorButtons"> <input type="submit" value="{@ok-title}" name="{@ok-id}"/> <xsl:text> </xsl:text> <input type="submit" value="{@cancel-title}" name="{@cancel-id}"/> </div> </td> </tr> </tfoot> <tbody> <tr style="vertical-align:top"> <td><xsl:apply-templates select="cat-category[@name='Filtro']"/></td> <td><xsl:apply-templates select="cat-category[@name='Filas']"/></td> <td><xsl:apply-templates select="cat-category[@name='Columnas']"/></td> </tr> </tbody> </table> </div> </xsl:template> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
<xsl:template match="cat-category[@name='Filtro']"> <div id="navitagorFiltro"> <select name="filterList" id="filterList" size="7" style="width:100%"> <xsl:for-each select="cat-item"> <option> <xsl:attribute name="value"> <xsl:value-of select="@id"/> <xsl:text>_</xsl:text> <xsl:value-of select="cat-button[@icon='row.png']/@id"/> <xsl:text>_</xsl:text> <xsl:value-of select="cat-button[@icon='column.png']/@id"/> </xsl:attribute> <xsl:value-of select="@name"/> <xsl:apply-templates select="slicer-value"/> </option> </xsl:for-each> </select> <input type="button" title="Enviar a filas" value="A filas" onclick="sendToAux('filterList', 1);"/> <input type="button" title="Enviar a columnas" value="A columnas" onclick="sendToAux('filterList', 2);"/> <input type="button" title="Editar filtro" value="Editar" onclick="sendToAux('filterList', 0);"/> </div> </xsl:template> <xsl:template match="cat-category[@name='Filas']"> <div id="navitagorFilas"> <select name="rowsList" id="rowsList" size="7" style="width:100%"> <xsl:for-each select="cat-item"> <option> <xsl:attribute name="value"> <xsl:value-of select="@id"/> <xsl:text>_</xsl:text> <xsl:value-of select="cat-button[@icon='column.png']/@id"/> <xsl:text>_</xsl:text> <xsl:value-of select="cat-button[@icon='filter.png']/@id"/> <xsl:text>_</xsl:text> <xsl:value-of select="move-button[@style='bwd']/@id"/> <xsl:text>_</xsl:text> <xsl:value-of select="move-button[@style='fwd']/@id"/> </xsl:attribute> <xsl:value-of select="@name"/> <xsl:apply-templates select="slicer-value"/> </option> </xsl:for-each> </select> <input type="button" title="Editar filtro" value="Editar" onclick="sendToAux('rowsList', 0);"/> <input type="button" title="Enviar a columnas" value="A columnas" onclick="sendToAux('rowsList', 1);"/> <input type="button" title="Quitar variable" value="Quitar" onclick="sendToAux('rowsList', 2);"/> <input type="button" title="Subir" value="Subir" onclick="sendToAux('rowsList', 3);"/> <input type="button" title="Bajar" value="Bajar" onclick="sendToAux('rowsList', 4);"/> </div> </xsl:template> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
<xsl:template match="cat-category[@name='Columnas']"> <div id="navitagorColumnas"> <select name="colsList" id="colsList" size="7" style="width:100%"> <xsl:for-each select="cat-item"> <option> <xsl:attribute name="value"> <xsl:value-of select="@id"/> <xsl:text>_</xsl:text> <xsl:value-of select="cat-button[@icon='row.png']/@id"/> <xsl:text>_</xsl:text> <xsl:value-of select="cat-button[@icon='filter.png']/@id"/> <xsl:text>_</xsl:text> <xsl:value-of select="move-button[@style='bwd']/@id"/> <xsl:text>_</xsl:text> <xsl:value-of select="move-button[@style='fwd']/@id"/> </xsl:attribute> <xsl:value-of select="@name"/> <xsl:apply-templates select="slicer-value"/> </option> </xsl:for-each> </select> <input type="button" title="Editar filtro" value="Editar" onclick="sendToAux('colsList', 0);"/> <input type="button" title="Enviar a columnas" value="A filas" onclick="sendToAux('colsList', 1);"/> <input type="button" title="Quitar variable" value="Quitar" onclick="sendToAux('colsList', 2);"/> <input type="button" title="Subir" value="Subir" onclick="sendToAux('colsList', 3);"/> <input type="button" title="Bajar" value="Bajar" onclick="sendToAux('colsList', 4);"/> </div> </xsl:template> <xsl:template match="slicer-value"> <span style="font-size:11px;color:#227722"> <xsl:text> (</xsl:text> <xsl:value-of select="@level"/> <xsl:text>=</xsl:text> <xsl:value-of select="@label"/> <xsl:text>)</xsl:text> </span> </xsl:template> </xsl:stylesheet> |
Pasos a realizar en tu proyecto para obtener ese nuevo interface gráfico:
Los pasos a realizar son muy sencillos, simplemente deberás cambiar la plantilla hierarchy-navigator.xsl
inicial por la se muestra en el tutorial.
Si al hacerlo no te funciona, es muy probable que sea porque tu página JSP tenga varios formularios… tendrás que hacer algún retoque para que tenga en cuenta esto.
Conclusiones
Aunque ahora parezca sencillo, llegar a esa conclusión me ha llevado un tiempo considerable de estudiar el modelo y las librerías de las que depende JPivot.. mirando el código fuente, pegándome con ello y barajando otras alternativas.
Pero bueno, espero que os sea de utilidad.
Un saludo.
Carlos García. Creador de MobileTest, un complemento educativo para los profesores y sus alumnos.