Jasper – Crosstab + nullable field

Bom dia galera,

Hoje vamos sair um pouco do mundo .Net e vamos entrar um pouquinho no Java,  Jasper Reports.

O problema

O problema é, temos um set de dados onde null é diferente de 0. O relatório já está pronto. Quando for null queremos que o campo fique branco e quando for 0 apareça o zero lá. E agora?

Solução: Campo object e um Custom Incrementer Factory baseado no JRIntegerIncrementerFactory

O objetivo é permitir que o campo seja nulo, e através de um text field expression, deixar em branco quando for nulo. Vamos precisar de uma Incrementer Factory para fazer o cálculo do total, pois sendo o field um object:

  • Default Incrementer Factory não utilizará o JRIntegerIncrementerFactory;
  • JRIntegerIncrementerFactory não pode ser utilizado pois seu construtor é nulo
  • JRIntegerIncrementerFactory não pode ser utilizado com superclasse porque é uma classe marcada como “final”.

Sendo assim vamos às alterações

Primeiro, altere o field que deseja como measure para object, conforme imagem

field_null

Vamos alterar o campo de texto do detail dentro da crosstab. Vamos marcar a propriedade Blank When null, deixar a expression class como string. (o campo detail é aquele que você deseja que seja a “coluna dinâmica”. Veja imagem:

detail_textfieldexpression

Nas propriedades do campo utilizado na seção Measures da crosstab podemos especificar um Incrementer Factory. Nesse campo incrementer factory colocamos a classe com o namespace completo para sua classe customizada.

Para desenvolver sua Custom Incrementer Factory, basta criar um novo projeto no eclipse, adicionar a referencia para o .jar do jasper: c:\Program Files\jasperreports-server-cp-4.7.0\apache-tomcat\webapps\jasperserver\WEB-INF\lib\jasperreports-x.y.z.jar

Exemplo de Custom Incrementer

package br.com.marcoslimagon.jasper

import net.sf.jasperreports.engine.fill.AbstractValueProvider;
import net.sf.jasperreports.engine.fill.JRAbstractExtendedIncrementer;
import net.sf.jasperreports.engine.fill.JRAbstractExtendedIncrementerFactory;
import net.sf.jasperreports.engine.fill.JRCalculable;
import net.sf.jasperreports.engine.fill.JRExtendedIncrementer;
import net.sf.jasperreports.engine.type.CalculationEnum;

/**
 * @author marcos.goncalves
 * @see http://grepcode.com/file_/repo1.maven.org/maven2/net.sf.jasperreports/jasperreports/4.7.1/net/sf/jasperreports/engine/fill/JRIntegerIncrementerFactory.java/?v=source
 */
public class JRNullableIntegerIncrementerFactory extends
		JRAbstractExtendedIncrementerFactory {

	protected static final Integer ZERO = Integer.valueOf(0);

	public JRNullableIntegerIncrementerFactory()
	{
	}

	@Override
	public JRExtendedIncrementer getExtendedIncrementer(CalculationEnum calculation) {
		JRExtendedIncrementer incrementer = null;

		switch (calculation)
		{
			case COUNT :
			{
				//incrementer = JRIntegerCountIncrementer.getInstance();
				break;
			}
			case SUM :
			{
				incrementer = JRNullableIntegerSumIncrementer.getInstance();
				break;
			}
			case AVERAGE :
			{
				//incrementer = JRIntegerAverageIncrementer.getInstance();
				break;
			}
			case LOWEST :
			case HIGHEST :
			{
				//incrementer = JRComparableIncrementerFactory.getInstance().getExtendedIncrementer(calculation);
				break;
			}
			case STANDARD_DEVIATION :
			{
				//incrementer = JRIntegerStandardDeviationIncrementer.getInstance();
				break;
			}
			case VARIANCE :
			{
				//incrementer = JRIntegerVarianceIncrementer.getInstance();
				break;
			}
			case DISTINCT_COUNT :
			{
				//incrementer = JRIntegerDistinctCountIncrementer.getInstance();
				break;
			}
			case SYSTEM :
			case NOTHING :
			case FIRST :
			default :
			{
				//incrementer = JRDefaultIncrementerFactory.getInstance().getExtendedIncrementer(calculation);
				break;
			}
		}

		return incrementer;
	}

}

/**
*
*/
final class JRNullableIntegerSumIncrementer extends JRAbstractExtendedIncrementer
{
	/**
	 *
	 */
	private static JRNullableIntegerSumIncrementer mainInstance = new JRNullableIntegerSumIncrementer();

	/**
	 *
	 */
	private JRNullableIntegerSumIncrementer()
	{
	}

	/**
	 *
	 */
	public static JRNullableIntegerSumIncrementer getInstance()
	{
		return mainInstance;
	}

	/**
	 *
	 */
	public Object increment(
		JRCalculable variable,
		Object expressionValue,
		AbstractValueProvider valueProvider
		)
	{

		Number value = (Number)variable.getIncrementedValue();
		Number newValue = (Number)expressionValue;

		if (newValue == null)
		{
			if (variable.isInitialized())
			{
				return null;
			}

			return value;
		}

		if (value == null || variable.isInitialized())
		{
			value = JRNullableIntegerIncrementerFactory.ZERO;
		}

		return Integer.valueOf(value.intValue() + newValue.intValue());
	}

	public Object initialValue()
	{
		//Starts with null value instead of zero
		//return JRNullableIntegerIncrementerFactory.ZERO;
		return null;
	}
}

OBS: Eu só deixei a soma funcionando. Todo o código se baseia na versão 4.7.1 da classe JRIntegerIncrementerFactory do código-fonte do Jasper.

Agora basta gerar o .Jar dessa classe e adicionar no iReport: Ferramentas > opções > classpath > Add jar

Adicione no relatório: JasperServer Report > Add Jar archive

Adicione o .jar gerado na pasta lib do servidor do jasper (não tenho certeza sobre a necessidade desse passo ainda)

Agora na crosstab do seu relatório, na seção Measures, deixe a Measure Class como Object e coloque sua IncrementerFactory (com namespace completo) conforme imagempropriedades_measure

Voilà! Quando executar a consulta, se o campo usado como measure/details for null ele exibirá branco, se vier 0 ele exibirá o 0. Ambos null e 0 não farão parte da soma do total.

Abraços! e até a próxima!!

Deixe uma resposta

O seu endereço de email não será publicado Campos obrigatórios são marcados *

Você pode usar estas tags e atributos de HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>