JODReports - Tutorial práctico. - Más allá de lo que ves

miércoles, 3 de abril de 2013

JODReports - Tutorial práctico.

Tras ver la gran aceptación que a tenido el tema de los reportes utilizando plantillas ODT y la librería JODReports y viendo la escasa documentación y ejemplos que se pueden encontrar en la red me dispongo a mostraros algunos ejemplos de utilización de esta herramienta así como algunas recomendaciones y posibles problemas que he tenido a lo largo de algunos proyectos con estos informes y la mejor forma que he encontrado de solucionarlo.
Este tutorial se divide en 2: en la primera mostraré la parte en java (básicamente coger la plantilla, enchufarle los datos y obtener el fichero final) y en la segunda, a mi entender, la mejor forma de construir una plantilla.
En el post anterior JODReports - Mini How to se describe la utilización básica de esta librería. Aquí repasaremos dichos ejemplos y ampliaremos con recomendaciones que por razones de desconocimiento no se mencionaron en el anterior.

1. JODReports: Sustitución de variables.

Para sustituir las variables en nuestra plantilla lo primero que debemos hacer es abrir nuestra plantilla, para ello utilizamos la siguiente línea:
DocumentTemplateFactory documentTemplateFactory = new DocumentTemplateFactory();

DocumentTemplate template = documentTemplateFactory.getTemplate(new File(ruta_fisica_plantilla/nombreFichero.odt));

Las variables a sustituir previamiente las hemos debido de cargar a un objeto Map
Map data = new HashMap();
data.put("nombre_variable", "valor_variable");
// añadir todas las variables a cambiar

Una vez tenemos la plantilla y las variables simplemente pasamos estas últimas a la plantilla y le indicamos el fichero de salida.
template.createDocument(data, new FileOutputStream(ruta_fisica_fichero_guardar//nombreFichero-final.odt));

Si queremos incluir imágenes en nuestra plantilla odt deberemos definir un ImageSource que también añadiremos a nuestro Map con las variables para que sea sustituida.
El objeto ImageSource se puede definir de las siguientes maneras:
ImageSource imagen = new RenderedImageSource(ImageIO.read(new File(ruta_fisica_imagen)));
ImageSource imagen = new FileImageSource(ruta_fisica_imagen);
ImageSource imagen = new ClasspathImageSource(ruta_fisica_imagen);

2. JODReports: Construcción de la plantilla.

Bueno llegados a este punto ya tenemos la parte de java desarrollada ahora vamos a construir la plantilla odt.
Recomendación MUY IMPORTANTE: Hay que intentar lo más posible hacer la plantilla enteramente desde el OpenOffice, si habéis leído el post anterior habréis observado que para introducir las sentencias de bucles o condicionales se recurre a abrir el fichero odt con winrar y editar el archivo content.xml esto es un error pues TODO el código que hayamos tecleado fuera de la estructura del documento si tenemos que editar la plantilla desde el OpenOffice para hacer alguna modificación en el contenido estático, al guardar todo será eliminado ya que el documento es reconstruido cada vez que guardamos.
Ahora empezamos a construir la plantilla. Lo primero será preparar todo el contenido estático del documento (contenido y formato). Es importante que el documento esté bien construido (por ejemplo: nada de 5 saltos de linea para conseguir la separación apropiada entre 2 párrafos nos vamos a propiedades del párrafo y le decimos que tenga una separación concreta con el siguiente o si necesitamos una página nueva hacemos un salto de página nada de salto de lineas hasta que salte de página).

Variables


Una vez que tenemos el contenido estático empezamos a introducir el contenido dinámico, lo primero y más sencillo las variables. Para insertar una variable simplemente ${nombre_variable}. Esta variable a de estar en nuestro Map principal. Esta variable puede ser un objeto simple (int, char, Integer, String, etc.) o un objeto complejo o un Map en tales casos para acceder a una propiedad (en el objeto) se hará ${nombre_variable.propiedad} o un value del objeto con ${nombre_variable.clave}.
Si necesitamos, por ejemplo, un contador dentro del documento o una variable podemos hacerlo de la siguiente manera:
[#assign contador = 1]
Acabamos de crear una variable "contador" con valor = 1. Para mostrarla simplemente ${contador}. Si necesitamos incrementarla, por ejemplo, lo podemos hacer de la siguiente manera :
[#assign contador = contado + 1]

Sentencia condicional


La sentencia condicional que tenemos disponible para mostrar u ocultar un bloque del documento es la siguiente.
[#if mi_variable = valor]
bloque de contenido...
[#else]
otro bloque...
[/#if]
¿Simple verdad? Ahora voy a explicar y a mostrar con un sencillo ejemplo un caso práctico de como utilizar esto correctamente, los posibles errores que podemos tener y como evitarlos o solucionarlos.
He aquí una captura de una plantilla con 3 párrafos (5 si contamos los 2 formados por la apertura y cierre del segundo if) y dos sentencias condicionales:
JODReports : ejemplo condicional if
En el ejemplo mostrado en la imagen, la primera condición de no cumplirse al generar el odt resultante hará que aunque el fichero se cree de forma correcta al tratar de abrirlo con OpenOffice nos de un error en la estructura. ¿la razón? la explico a continuación. El código interno de la plantilla tras guardarla para estos párrafos sería el siguiente:

<text:p text:style-name="P1">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin eu varius diam. Curabitur quis tellus turpis. Nunc non nibh massa. Vestibulum et [#if variableUno = 1] <text:span text:style-name="T1"> lorem sed arcu hendrerit[/#if]</text:span> suscipit in ut mauris. Maecenas est ipsum, feugiat at rhoncus eget, scelerisque quis felis. Vestibulum elementum pellentesque pharetra. Donec commodo volutpat nunc vel cursus.
</text:p>
<text:p text:style-name="P1">
[#if variableDos = 1]
</text:p>
<text:p text:style-name="P1">
Nullam lorem elit, tempor fermentum sodales nec, ultricies a leo. Fusce vestibulum nisl eget libero pulvinar in accumsan libero pulvinar. Sed placerat tortor in arcu laoreet id convallis turpis rhoncus. Suspendisse sit amet mollis ante.
</text:p>
<text:p text:style-name="P1">
[/#if]
</text:p>
<text:p text:style-name="P1">
Aliquam vel lorem ligula. Donec convallis, ligula id dictum imperdiet, leo diam congue quam, in eleifend est neque eu tellus.
</text:p>

El código anterior es el que veríamos si abriéramos el odt de la plantilla con winrar y editáramos el content.xml. En el se pueden apreciar perfectamente los 5 párrafos que componen este documento (<text:p></text:p>). Como es un ejemplo muy simple el estilo para los 5 párrafos es el mismo (text:style-name="P1"). Para aplicar un estilo dentro del párrafo en linea se utiliza <text:span></text:span>.
Una vez aclarado esto podemos observar que para dar el formato de negrita en el documento se ha utilizado esta etiqueta span. ¿cuál es el problema con la primera sentencia? Si se cumple no hay problema, ahora si la condición no se cumple al generar el odt resultante se eliminará todo aquello dentro del [#if][/if] por lo que el código del párrafo generado será el siguiente:

<text:p text:style-name="P1">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin eu varius diam. Curabitur quis tellus turpis. Nunc non nibh massa. Vestibulum et </text:span> suscipit in ut mauris. Maecenas est ipsum, feugiat at rhoncus eget, scelerisque quis felis. Vestibulum elementum pellentesque pharetra. Donec commodo volutpat nunc vel cursus.
</text:p>
Como se pude observar queda por ahí perdida una etiqueta de cierre del span que daba la formato al a la porción de texto dentro del párrafo. Esto va a producir un error al abrir el documento con OpenOffice pues la estructura de este no es correcta. Para evitar estos errores simplemente debemos de estar atentos al construir nuestra plantilla. Si el cierre del if [/#if] no hubiera estado en negrita significaría que esta fuera de ese span de formato y por lo tanto se eliminaría por completo todo el span y no se habría roto la estructura del documento. Y la plantilla sería correcta.
En la segunda sentencia no habría problemas de no cumplirse pues el código resultante sería correcto. Marco con un fondo más oscuro lo susceptible a desaparecer en el código si no se cumpliera la segunda condición.
La etiqueta de párrafo del sentencia [#if] en caso de no cumplirse la condición no se queda abierta ya que se cierra con la etiqueta de cierre del párrafo del cierre de la sentencia [/#if].

<text:p text:style-name="P1">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin eu varius diam. Curabitur quis tellus turpis. Nunc non nibh massa. Vestibulum et [#if variableUno = 1] <text:span text:style-name="T1"> lorem sed arcu hendrerit[/#if]</text:span> suscipit in ut mauris. Maecenas est ipsum, feugiat at rhoncus eget, scelerisque quis felis. Vestibulum elementum pellentesque pharetra. Donec commodo volutpat nunc vel cursus.
</text:p>
<text:p text:style-name="P1">
[#if variableDos = 1]
</text:p>
<text:p text:style-name="P1">
Nullam lorem elit, tempor fermentum sodales nec, ultricies a leo. Fusce vestibulum nisl eget libero pulvinar in accumsan libero pulvinar. Sed placerat tortor in arcu laoreet id convallis turpis rhoncus. Suspendisse sit amet mollis ante.
</text:p>
<text:p text:style-name="P1">
[/#if]
</text:p>
<text:p text:style-name="P1">
Aliquam vel lorem ligula. Donec convallis, ligula id dictum imperdiet, leo diam congue quam, in eleifend est neque eu tellus.
</text:p>
Resumiendo aunque no tengamos que editar el content.xml si que tenemos que construir la plantilla teniendo presente el código que se va a generar por debajo.

Iteración de listas


Para la iteración de objetos tipo List tenemos en JODReports la sentencia [#list nuestra_lista as item][/#list] y se utiliza de la siguiente manera:

Para iterar un List de Strings
[#list mi_lista_string as cadena]
${cadena}
[/#list]

Para iterar un list de objetos con sus propiedades
[#list mi_lista_object as item]
${item.propiedad}
[/#list]
A continuación vamos a ver unos ejemplos de iteración de listas, la lista estará formada por un listado de objetos Producto como el que se muestra a continuación.
public class Producto {
    private int id;
    private String nombre;
    private int cantidad;
    private float precioUnidad;

   // Constructores
   // ...
        
   // Getters and Setters
   // ...

}
Ahora vamos a ver 4 ejemplos básicos de iteración de listas, el porque de construir así la plantilla y el código que produce. Empezaremos viendo la captura de la plantilla con los 4 ejemplos.
JODReports : ejemplos de list
Ejemplo 1
En el primer ejemplo vamos a iterar un list en línea con el texto (dentro de un párrafo). En este caso la única consideración a tener en cuenta es la misma que vimos en el primer ejemplo de la sentencia condicional, es decir, tenemos que intentar prevenir que el list pueda romper algún estilo. Veamos el código interno producido y la parte que se va a repetir con cada iteración (con fondo más oscuro).

<text:p text:style-name="Standard">Ejemplo 1:</text:p>
<text:p text:style-name="Standard"> A continuación se mencionan el nombre de todos los productos:[#list productos as item] ${item.nombre}, [/#list]. Las propiedades de cada producto se detallan en cada tabla de producto.
</text:p>
Ejemplo 2
En este segundo ejemplo vamos a mostrar todas las propiedades del elemento Producto en una tabla individual por cada elemento o iteración. En principio tampoco contempla ningún tipo de complicación ya que la iteración se va a realizar sobre una estructura (una tabla, podría ser un párrafo, etc) completa por lo que no tendremos problemas de que se rompa la estructura aunque no contenga elementos la lista ya que en ese caso la etiqueta que se abre antes de la definición del [#list] se cerraría con la etiqueta de cierre de la etiqueta que contiene el cierre del list [/#list] (al no tener elementos que iterar todo lo contenido entre [#list] y [/#list] se eliminará). Veamos el código interno producido y la parte que se va a repetir con cada iteración (con fondo más oscuro).

<text:p text:style-name="Standard">Ejemplo 2:</text:p>
<text:p text:style-name="Standard">
  [#list productos as item]
</text:p>
<table:table table:name="Tabla1" table:style-name="Tabla1">
  <table:table-column   table:style-name="Tabla1.A"/>
  <table:table-column   table:style-name="Tabla1.B"/>
  <table:table-row>
    <table:table-cell table:style-name="Tabla1.A1" office:value-type="string">
      <text:p text:style-name="P1">
        Id
      </text:p>
    </table:table-cell>
    <table:table-cell table:style-name="Tabla1.B1" office:value-type="string">
      <text:p text:style-name="Table_20_Contents">
         ${item.id}
      </text:p>
    </table:table-cell>
  </table:table-row>
  <table:table-row>
    <table:table-cell table:style-name="Tabla1.A2" office:value-type="string">
      <text:p text:style-name="P1">
        Nombre
      </text:p>
    </table:table-cell>
    <table:table-cell table:style-name="Tabla1.B2" office:value-type="string">
      <text:p text:style-name="Table_20_Contents">
        ${item.nombre}
      </text:p>
    </table:table-cell>
  </table:table-row>
  <table:table-row>
    <table:table-cell table:style-name="Tabla1.A2" office:value-type="string">
      <text:p text:style-name="P1">
        Cantidad
      </text:p>
    </table:table-cell>
    <table:table-cell table:style-name="Tabla1.B2" office:value-type="string">
      <text:p text:style-name="Table_20_Contents">
        ${item.cantidad}
      </text:p>
    </table:table-cell>
  </table:table-row>
  <table:table-row>
    <table:table-cell table:style-name="Tabla1.A2" office:value-type="string">
      <text:p text:style-name="P1">
        Precio
      </text:p>
    </table:table-cell>
    <table:table-cell table:style-name="Tabla1.B2" office:value-type="string">
      <text:p text:style-name="Table_20_Contents">
        ${item.precioUnidad}/ Unidad
      </text:p>
    </table:table-cell>
  </table:table-row>
</table:table>
<text:p text:style-name="Standard">
  
[/#list]
</text:p>
Ejemplo 3
En este tercer ejemplo vamos a iterar los elementos Producto del list dentro de una tabla sin cabecera de tal forma que cada elemento este en una fila y cada propiedad en una columna. La definición del list se hace en la primera columna de la tabla que tendrá los bordes superior, izquierda e inferior sin pintar u ocultos para que una vez generado el informe no se aprecie esta columna. En la segunda columna y en adelante vamos colocando las propiedades del objeto como se muestra. El cierre del list se hace en la primera columna de la segunda fila, ocultaremos también los bordes oportunos para hacer desaparecer esta fila al generar el informe. Esta forma especial de construir el list dentro de la tabla hará que las iteraciones hagan crecer la tabla sin romper su estructura tengamos 0, 1 o n elementos en el list. Veamos el código interno producido y la parte que se va a repetir con cada iteración (con fondo más oscuro).

<text:p text:style-name="Standard">
  Ejemplo 3:
</text:p>
<table:table table:name="Tabla2" table:style-name="Tabla2">
  <table:table-column table:style-name="Tabla2.A"/>
  <table:table-column table:style-name="Tabla2.B"/>
  <table:table-column table:style-name="Tabla2.C"/>
  <table:table-column table:style-name="Tabla2.D"/>
  <table:table-column table:style-name="Tabla2.E"/>
  <table:table-row>
    <table:table-cell table:style-name="Tabla2.A1" office:value-type="string">
      <text:p text:style-name="P2">
        [#list productos as item]
      </text:p>
    </table:table-cell>
    <table:table-cell table:style-name="Tabla2.B1" office:value-type="string">
      <text:p text:style-name="Table_20_Contents">
         ${item.id}
      </text:p>
    </table:table-cell>
    <table:table-cell table:style-name="Tabla2.B1" office:value-type="string">
      <text:p text:style-name="Table_20_Contents">
        ${item.nombre}
      </text:p>
    </table:table-cell>
    <table:table-cell table:style-name="Tabla2.B1" office:value-type="string">
      <text:p text:style-name="Table_20_Contents">
        ${item.cantidad}
      </text:p>
    </table:table-cell>
    <table:table-cell table:style-name="Tabla2.E1" office:value-type="string">
      <text:p text:style-name="Table_20_Contents">
        ${item.precioUnidad} / Unidad
      </text:p>
    </table:table-cell>
  </table:table-row>
  <table:table-row>
    <table:table-cell table:style-name="Tabla2.A1" office:value-type="string">
      <text:p text:style-name="P2">
        
[/#list]
      </text:p>
    </table:table-cell>
    <table:table-cell table:style-name="Tabla2.A1" office:value-type="string">
      <text:p text:style-name="P2"/>
    </table:table-cell>
    <table:table-cell table:style-name="Tabla2.A1" office:value-type="string">
      <text:p text:style-name="P2"/>
    </table:table-cell>
    <table:table-cell table:style-name="Tabla2.A1" office:value-type="string">
      <text:p text:style-name="P2"/>
    </table:table-cell>
    <table:table-cell table:style-name="Tabla2.A1" office:value-type="string">
      <text:p text:style-name="P2"/>
    </table:table-cell>
  </table:table-row>
</table:table>
Ejemplo 4
El cuarto y último ejemplo de iterar listas es muy parecido al anterior se iteran las elementos rellenando una tabla solo que en este ejemplo la tabla lleva una cabecera en la primera fila. Al igual que en el ejemplo anterior el list se abrirá y cerrará en una columna a la cual ocultaremos los bordes oportunos para que al generar el informe no se visualice solo que en este caso la columna no será la primera sino la última. En la última columna de la cabecera abrimos el list, en la siguiente fila iteramos las propiedades del objeto y en la última columna de esta fila cerramos el list. Esta forma especial de construir el list dentro de la tabla hará que las iteraciones hagan crecer la tabla sin romper su estructura tengamos 0, 1 o n elementos en el list. Veamos el código interno producido y la parte que se va a repetir con cada iteración (con fondo más oscuro).

<text:p text:style-name="Standard">
  Ejemplo 4:
</text:p>
<table:table table:name="Tabla3" table:style-name="Tabla3">
  <table:table-column table:style-name="Tabla3.A"/>
  <table:table-column table:style-name="Tabla3.B"/>
  <table:table-column table:style-name="Tabla3.C"/>
  <table:table-column table:style-name="Tabla3.D"/>
  <table:table-column table:style-name="Tabla3.E"/>
  <table:table-row>
    <table:table-cell table:style-name="Tabla3.A1" office:value-type="string">
      <text:p text:style-name="P4">
        ID:
      </text:p>
    </table:table-cell>
    <table:table-cell table:style-name="Tabla3.A1" office:value-type="string">
      <text:p text:style-name="P4">
        NOMBRE:
      </text:p>
    </table:table-cell>
    <table:table-cell table:style-name="Tabla3.A1" office:value-type="string">
      <text:p text:style-name="P4">
        CANTIDAD:
      </text:p>
    </table:table-cell>
    <table:table-cell table:style-name="Tabla3.D1" office:value-type="string">
      <text:p text:style-name="P4">
        PRECIO / UNIDAD:
      </text:p>
    </table:table-cell>
    <table:table-cell table:style-name="Tabla3.E1" office:value-type="string">
      <text:p text:style-name="P2">
        [#list productos as item]
      </text:p>
    </table:table-cell>
  </table:table-row>
  <table:table-row>
    <table:table-cell table:style-name="Tabla3.A2" office:value-type="string">
      <text:p text:style-name="Table_20_Contents">
        ${item.id}:
      </text:p>
    </table:table-cell>
    <table:table-cell table:style-name="Tabla3.A2" office:value-type="string">
      <text:p text:style-name="Table_20_Contents">
        ${item.nombre}:
      </text:p>
    </table:table-cell>
    <table:table-cell table:style-name="Tabla3.A2" office:value-type="string">
      <text:p text:style-name="Table_20_Contents">
        ${item.cantidad}:
      </text:p>
    </table:table-cell>
    <table:table-cell table:style-name="Tabla3.D2" office:value-type="string">
      <text:p text:style-name="Table_20_Contents">
        ${item.precioUnidad} / Unidad:
      </text:p>
    </table:table-cell>
    <table:table-cell table:style-name="Tabla3.E1" office:value-type="string">
      <text:p text:style-name="Table_20_Contents">
        
[/#list]:
      </text:p>
    </table:table-cell>
  </table:table-row>
</table:table>

Imágenes


Para incluir imágenes de forma dinámica en nuestra plantilla simplemente tenemos que insertar uma imagen abrir sus propiedades y en la pestaña "Opciones" indicamos para "Nombre" jooscript.image(nombre_variable) tal y como se muestra en la imagen.
JODReports : inclusión de imágenes
Y hasta aquí este post de JODReports y la construcción de plantillas desde OpenOffice. Espero que sirva si os estáis peleando con los informes. No olvidéis si os ha gustado y os ha servido de ayuda dejar un comentario y compartir. Cualquier duda no dudéis en preguntarla trataré de responder a la mayor brevedad posible.
Salu2

14 comentarios:

  1. Te lo has trabajado. Te lo digo en serio. Pero tal vez el tema es más complejo de lo que parece. Sobre todo para quien no conozca bien los entresijos de OpenOffice y cómo construir esas plantillas.

    Te puedo decir que me duele la cabeza de tanto estrujármela. Tal vez sea una nimiedad pero me está desesperando el jodreports este. Lo he intentado con varios y cada cual tiene sus problemas, o te faltan librerías, o tienes que añadir doscientas.

    Por ponerte un ejemplo lo sencillo sí me funciona Crtl+F2 jooscript arriba y ${nombre} abajo. Todo lo que sea salir de hay no hay manera.

    ¿Cómo hago para colocar una imagen? ¿Puedes explicarlo mejor? Por que la página de esta gente, de verdad que se agradece, pero chico es de pena la forma en la que se explican algunas veces. Pierdo más tiempo experimentando y buscando en internet que en terminar la aplicación web.
    Tú lo explicas mejor, pero o soy muy corto o se me escapa algo que no sé, tal vez relacionado con la macro que se instala en openoffice, yo la instalo, pero me da que tal vez no funcione.
    Por cierto, la marca verde que observo en algunas plantillas, no he podido reproducirla en mis archivos odt. ¿Qué es esa marca?

    Perdón por el tono, pero te juro que me tiene desesperado el jodreport y la escasa y mala documentación al respecto. Es de agradecer el soft libre, pero si es al precio de malgastar docenas de horas, tal vez sería mejor que no sea tan libre.

    ResponderEliminar
    Respuestas
    1. Buenas Tron.
      Lo de la imagen debería ser sencillo. Inserta una imagen en tu plantilla y abre sus propiedades, en la pestaña "opciones" busca la propiedad "Nombre" y literalmente escribe "jooscript.image()" y dentro de los paréntesis el nombre de la variable sin llaves "{}" ni el símbolo del dolar "$".

      El tipo de objeto que debes dejar caer a tu plantilla es un ImageSource. Puedes crear el objeto de esta forma:
      ImageSource imagen = new FileImageSource(ruta_fisica_imagen);

      Si dejas caer la imagen de esta forma:
      map.put("miImagen", imagen); // imagen es el ImageSource creado previamente.

      El "Nombre" de la imagen en la plantilla debe de quedar de la siguiente manera:
      jooscript.image(miImagen)

      En cuanto a la marca verde que me indicas no se a que te refieres.

      Eliminar
  2. Antes de usar openoffice usabamos fop y eso si era un infierno.

    Esto es mucho más ligero.

    Comentaré con Manuel (autor de la entrada) que te eche un cable.

    Un saludo.

    ResponderEliminar
  3. Hola, muy útil la verdad para iniciarse en este tema, me sirvió para la iteración de listas.

    Se me ha presentado un problema que no sé como plantear la solución, a ver si alguien se le ocurre algo y tiene la amabilidad de "alumbrarme".

    En la aplicación se carga una lista de vehículos, esto se hace sin problemas pasando la lista de objetos e iterando sobre ella. Vale, hasta aquí todo correcto. Pero cómo se plantearía si cada vehículo el cual estoy iterando, tuviese una lista de ocupantes (personas que van dentro). He intentado meter directamente una lista de ocupantes dentro de las etiquetas de la lista de vehículos ya existente, pero falla, se liará con los finales de list [#/list]

    [#list data.tables.vehiculos.row as vehiculo_ciclo]
    Vehículo $$459$$
    [#list data.tables.ocupantes.row as ocupante_ciclo]
    Ocupante $$481$$
    [/#list]
    [/#list]

    Muchas gracias

    ResponderEliminar
  4. se me olvidó marcar para que me avise al correo

    ResponderEliminar
    Respuestas
    1. Buenas titofiest. Puesto que los ocupantes dependen del vehículo prueba a colocar estos como una propiedad del objeto vehículo_ciclo. Te debería quedar algo así:

      [#list data.tables.vehiculos.row as vehiculo_ciclo]
      Vehículo $$459$$
      [#list vehiculo_ciclo.ocupantesRow as ocupante_ciclo]
      Ocupante $$481$$
      [/#list]
      [/#list]

      De esta forma por cada vehículo se iterará una lista de ocupantes.
      Salu2 espero haberte ayudado!

      Eliminar
  5. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  6. Titofiest, espero te sea útil el comentario de Manuel. Sin duda es un crack en la materia.

    Un saludo.

    ResponderEliminar
  7. Muchas gracias por la solución y por la rapidez!

    Mi duda era la de poder meter un list dentro de otro list, que no sé porque me estaba produciendo un comportamiento raro, pero eso ya lo solucioné antes de ver tu respuesta.

    En lo que me ha ayudado tu respuesta, es que no sabía si se podría pasar un list dentro de una propiedad de otro objeto que estaba ya pasando (meter la lista de ocupantes dentro del vehículo), pensaba que tan solo podía pasar strings, y lo que hacía cada vez que quería incluir un listado era meterlo como addTable independiente, haciendo más compleja la relación de los ocupantes con cada vehículo, ahora veo que no, que addObject y dentro de ese objeto, puedo meter los listados.

    De nuevo, gracias!

    ResponderEliminar
    Respuestas
    1. Mucho mejor así No?

      Me alegra saber que lo has solucionado.


      Un saludo.

      Eliminar
  8. Pues tengo una nueva pega que me tiene algo mosqueado, no sé si es el mejor sitio para ponerlo, disculpas si no fuera así. Explico mi caso.

    En la aplicación en la que trabajo, se fusionan varios .odt, hasta ahora no he tenido ningún problema usando la clase FusionOODocument.
    Algunas de las hojas de un .odt en concreto, a partir de ahora X, tienen un formato algo más especial, con varios checkboxs dentro de formularios.
    Entonces si fusiono X con cualquier otro documento, algunos de dichos checkboxs se pierden o lo que es peor, se cambian por otro tipo de elemento. Pinchando en la opción de navegar por el formulario del documento generado tras la fusión, veo que no hay ningún objeto en el formulario.
    Si por el contrario, fusiono X con sí mismo, no me aparece este problema, se mantienen los formularios y los objetos contenidos en ellos.

    No sé si llegué a explicarme.

    Así que el problema es como solucionar esta situación para que al fusionar no se pierdan esos checkbox

    Muchas gracias de antemano!

    ResponderEliminar
  9. Hola chicos,
    Me encuentro investigando sobre el tema porque en este momento en la aplicación que tenemos hacemos todo esto con RTF. El desarrollo de la librería parece un poco parada, alguien sabe si utilizar JODReports es viable? en caso contrario me pueden sugerir alguna otra?

    ResponderEliminar
  10. Buenas!
    Estamos intentando en el trabajo generar la plantilla con un servicio RMI apoyado en otra aplicación "Plantill@" de la Junta de Andalucía. La cuestión/problema es que cuando le envío el MAP de datos con el objeto imagen (RenderedImageSource) me salta un error al generar el documento:

    error marshalling arguments; nested exception is: java.io.NotSerializableException: net.sf.jooreports.templates.image.RenderedImageSource


    Hemos creado nuestra propia clase serializable (MyRenderedImageSource) que extiende de RenderedImageSource y ahora el error es:

    RemoteException occurred in server thread; nested exception is: java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is: java.lang.ClassNotFoundException: com.viavansi.tramitacion.util.MyRenderedImageSource (no security manager: RMI class loader disabled)

    ¿Alguna ayuda? Gracias.

    ResponderEliminar