/* * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software * Foundation. * * You should have received a copy of the GNU Lesser General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html * or from the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * Copyright (c) 2001 - 2013 Object Refinery Ltd, Pentaho Corporation and Contributors.. All rights reserved. */ package org.pentaho.reporting.engine.classic.core.wizard; import java.awt.Component; import java.awt.Image; import java.awt.Shape; import java.io.File; import java.net.URL; import java.sql.Blob; import java.sql.Time; import java.text.DateFormat; import java.text.DecimalFormat; import java.text.NumberFormat; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; import java.util.HashSet; import org.pentaho.reporting.engine.classic.core.AbstractReportDefinition; import org.pentaho.reporting.engine.classic.core.AttributeNames; import org.pentaho.reporting.engine.classic.core.Band; import org.pentaho.reporting.engine.classic.core.Element; import org.pentaho.reporting.engine.classic.core.Group; import org.pentaho.reporting.engine.classic.core.ImageContainer; import org.pentaho.reporting.engine.classic.core.MetaAttributeNames; import org.pentaho.reporting.engine.classic.core.ReportDefinition; import org.pentaho.reporting.engine.classic.core.ReportProcessingException; import org.pentaho.reporting.engine.classic.core.filter.types.ContentFieldType; import org.pentaho.reporting.engine.classic.core.filter.types.DateFieldType; import org.pentaho.reporting.engine.classic.core.filter.types.LabelType; import org.pentaho.reporting.engine.classic.core.filter.types.NumberFieldType; import org.pentaho.reporting.engine.classic.core.filter.types.TextFieldType; import org.pentaho.reporting.engine.classic.core.function.Expression; import org.pentaho.reporting.engine.classic.core.metadata.ElementType; import org.pentaho.reporting.engine.classic.core.metadata.ExpressionMetaData; import org.pentaho.reporting.engine.classic.core.metadata.ExpressionRegistry; import org.pentaho.reporting.engine.classic.core.style.ElementStyleKeys; import org.pentaho.reporting.engine.classic.core.util.IntegerCache; import org.pentaho.reporting.libraries.formatting.FastMessageFormat; public class AutoGeneratorUtility { private AutoGeneratorUtility() { } public static Group[] getGroups( final ReportDefinition definition ) { final int groupCount = definition.getGroupCount(); final Group[] groups = new Group[groupCount]; for ( int i = 0; i < groupCount; i++ ) { final Group group = definition.getGroup( i ); groups[i] = group; } return groups; } public static String generateUniqueExpressionName( final DataSchema dataSchema, final String pattern, final String[] extraColumns ) throws ReportProcessingException { final FastMessageFormat fastMessageFormat = new FastMessageFormat( pattern ); if ( fastMessageFormat.getSubFormatCount() == 0 ) { throw new IllegalArgumentException(); } final HashSet<String> names = new HashSet<String>( Arrays.asList( dataSchema.getNames() ) ); for ( int i = 0; i < extraColumns.length; i++ ) { names.add( extraColumns[i] ); } final Object[] data = new Object[1]; int i = 0; // call me at any time if you have more than 32000 functions of the same name-pattern in a single report. while ( i < Short.MAX_VALUE ) { data[0] = IntegerCache.getInteger( i ); final String s = fastMessageFormat.format( data ); if ( names.contains( s ) == false ) { return s; } i += 1; } throw new ReportProcessingException( "Unable to create a unique name for the given pattern" ); } public static String generateUniqueExpressionName( final DataSchema dataSchema, final String pattern, final AbstractReportDefinition extraColumns ) throws ReportProcessingException { final FastMessageFormat fastMessageFormat = new FastMessageFormat( pattern ); if ( fastMessageFormat.getSubFormatCount() == 0 ) { throw new IllegalArgumentException(); } final HashSet<String> names = new HashSet<String>( Arrays.asList( dataSchema.getNames() ) ); final Expression[] expressions = extraColumns.getExpressions().getExpressions(); for ( int i = 0; i < expressions.length; i++ ) { final Expression expression = expressions[i]; names.add( expression.getName() ); } final Object[] data = new Object[1]; int i = 0; // call me at any time if you have more than 32000 functions of the same name-pattern in a single report. while ( i < Short.MAX_VALUE ) { data[0] = IntegerCache.getInteger( i ); final String s = fastMessageFormat.format( data ); if ( names.contains( s ) == false ) { return s; } i += 1; } throw new ReportProcessingException( "Unable to create a unique name for the given pattern" ); } /** * Computes a set of field widths. The input-width definitions can be a mix of absolute and relative values; the * resulting widths are always relative values. If the input width is null or zero, it is assumed that the field wants * to have a generic width. * * @param fieldDescriptions * @param pageWidth * @return */ public static float[] computeFieldWidths( final Float[] fieldDescriptions, final float pageWidth ) { final float[] resultWidths = new float[fieldDescriptions.length]; float definedWidth = 0; int definedNumberOfFields = 0; for ( int i = 0; i < fieldDescriptions.length; i++ ) { final Number number = fieldDescriptions[i]; if ( number != null && number.floatValue() != 0 ) { if ( number.floatValue() < 0 ) { // a fixed value .. resultWidths[i] = number.floatValue(); definedNumberOfFields += 1; definedWidth += number.floatValue(); } else { final float absValue = number.floatValue(); final float relativeValue = -absValue * 100 / pageWidth; resultWidths[i] = relativeValue; definedNumberOfFields += 1; definedWidth += relativeValue; } } } if ( definedNumberOfFields == fieldDescriptions.length ) { // we are done, all fields are defined. return resultWidths; } if ( definedNumberOfFields == 0 ) { // the worst case, no element provides a weight .. // therefore all fields have the same proportional width. Arrays.fill( resultWidths, -( 100 / fieldDescriptions.length ) ); return resultWidths; } final float availableSpace = -100 - definedWidth; if ( availableSpace > 0 ) { // all predefined fields already fill the complete page. There is no space left for the // extra columns. return resultWidths; } final float avgSpace = availableSpace / ( fieldDescriptions.length - definedNumberOfFields ); for ( int i = 0; i < resultWidths.length; i++ ) { final float width = resultWidths[i]; if ( width == 0 ) { resultWidths[i] = avgSpace; } } return resultWidths; } public static Element generateFooterElement( final Class aggregationType, final ElementType targetType, final String group, final String fieldName ) { if ( aggregationType == null ) { final Element footerValueElement = new Element(); footerValueElement.setElementType( new LabelType() ); footerValueElement.setAttribute( AttributeNames.Core.NAMESPACE, AttributeNames.Core.VALUE, "" ); footerValueElement.setAttribute( AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.ALLOW_METADATA_STYLING, Boolean.TRUE ); footerValueElement.setAttribute( AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.ALLOW_METADATA_ATTRIBUTES, Boolean.FALSE ); return footerValueElement; } final Element element = generateDetailsElement( fieldName, targetType ); element.setAttribute( AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.AGGREGATION_TYPE, aggregationType ); element.setAttribute( AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.AGGREGATION_GROUP, group ); return element; } public static Element generateHeaderElement( final String fieldName ) { final Element headerElement = new Element(); headerElement.getStyle().setStyleProperty( ElementStyleKeys.DYNAMIC_HEIGHT, Boolean.TRUE ); headerElement.setElementType( new LabelType() ); headerElement.setAttribute( AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.LABEL_FOR, fieldName ); headerElement.setAttribute( AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.ALLOW_METADATA_STYLING, Boolean.TRUE ); headerElement.setAttribute( AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.ALLOW_METADATA_ATTRIBUTES, Boolean.TRUE ); return headerElement; } public static Element generateDetailsElement( final String fieldName, final ElementType targetType ) { final Element detailsElement = new Element(); detailsElement.getStyle().setStyleProperty( ElementStyleKeys.DYNAMIC_HEIGHT, Boolean.TRUE ); detailsElement.setElementType( targetType ); detailsElement.setAttribute( AttributeNames.Core.NAMESPACE, AttributeNames.Core.FIELD, fieldName ); detailsElement.setAttribute( AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.ALLOW_METADATA_STYLING, Boolean.TRUE ); detailsElement.setAttribute( AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.ALLOW_METADATA_ATTRIBUTES, Boolean.TRUE ); return detailsElement; } public static Number createFieldWidth( final DataAttributes attributes, final DataAttributeContext context ) { return (Number) attributes.getMetaAttribute( MetaAttributeNames.Formatting.NAMESPACE, MetaAttributeNames.Formatting.DISPLAY_SIZE, Number.class, context ); } public static String createFieldName( final DataAttributes attributes, final DataAttributeContext context ) { return (String) attributes.getMetaAttribute( MetaAttributeNames.Core.NAMESPACE, MetaAttributeNames.Core.NAME, String.class, context ); } public static ElementType createFieldType( final DataAttributes attributes, final DataAttributeContext context ) { if ( attributes == null ) { return new TextFieldType(); } final Class type = (Class) attributes.getMetaAttribute( MetaAttributeNames.Core.NAMESPACE, MetaAttributeNames.Core.TYPE, Class.class, context ); return createFieldType( type ); } public static ElementType createFieldType( final Class type ) { final ElementType elementType; if ( Number.class.isAssignableFrom( type ) ) { elementType = new NumberFieldType(); } else if ( Date.class.isAssignableFrom( type ) ) { elementType = new DateFieldType(); } else if ( byte[].class.isAssignableFrom( type ) || Blob.class.isAssignableFrom( type ) || File.class.isAssignableFrom( type ) || URL.class.isAssignableFrom( type ) || Image.class.isAssignableFrom( type ) || Shape.class.isAssignableFrom( type ) || Component.class.isAssignableFrom( type ) || ImageContainer.class.isAssignableFrom( type ) ) { elementType = new ContentFieldType(); } else { elementType = new TextFieldType(); } return elementType; } public static String computeFormatString( final DataAttributes attributes, final DataAttributeContext context ) { if ( attributes == null ) { throw new NullPointerException(); } if ( context == null ) { throw new NullPointerException(); } final Class type = (Class) attributes.getMetaAttribute( MetaAttributeNames.Core.NAMESPACE, MetaAttributeNames.Core.TYPE, Class.class, context ); final Boolean currency = (Boolean) attributes.getMetaAttribute( MetaAttributeNames.Numeric.NAMESPACE, MetaAttributeNames.Numeric.CURRENCY, Boolean.class, context ); final Number scale = (Number) attributes.getMetaAttribute( MetaAttributeNames.Numeric.NAMESPACE, MetaAttributeNames.Numeric.SCALE, Number.class, context ); final Number precision = (Number) attributes.getMetaAttribute( MetaAttributeNames.Numeric.NAMESPACE, MetaAttributeNames.Numeric.PRECISION, Number.class, context ); if ( java.sql.Date.class.isAssignableFrom( type ) ) { // this includes timestamp .. final DateFormat dateFormat = DateFormat.getDateInstance( DateFormat.DEFAULT, context.getLocale() ); if ( dateFormat instanceof SimpleDateFormat ) { final SimpleDateFormat sdf = (SimpleDateFormat) dateFormat; return sdf.toPattern(); } // we cannot come up with a sensible default .. return null; } else if ( Time.class.isAssignableFrom( type ) ) { // this includes timestamp .. final DateFormat dateFormat = DateFormat.getTimeInstance( DateFormat.DEFAULT, context.getLocale() ); if ( dateFormat instanceof SimpleDateFormat ) { final SimpleDateFormat sdf = (SimpleDateFormat) dateFormat; return sdf.toPattern(); } // we cannot come up with a sensible default .. return null; } else if ( Date.class.isAssignableFrom( type ) ) { // this includes timestamp .. final DateFormat dateFormat = DateFormat.getDateTimeInstance( DateFormat.DEFAULT, DateFormat.DEFAULT, context.getLocale() ); if ( dateFormat instanceof SimpleDateFormat ) { final SimpleDateFormat sdf = (SimpleDateFormat) dateFormat; return sdf.toPattern(); } // we cannot come up with a sensible default .. return null; } else if ( Number.class.isAssignableFrom( type ) ) { if ( Boolean.TRUE.equals( currency ) ) { final NumberFormat format = NumberFormat.getCurrencyInstance( context.getLocale() ); if ( format instanceof DecimalFormat ) { final DecimalFormat decimalFormat = (DecimalFormat) format; return decimalFormat.toPattern(); } } final DecimalFormat format = new DecimalFormat(); if ( scale != null && precision != null ) { format.setMaximumFractionDigits( scale.intValue() ); format.setMinimumFractionDigits( scale.intValue() ); format.setMaximumIntegerDigits( precision.intValue() - scale.intValue() ); format.setMinimumIntegerDigits( 1 ); } return format.toPattern(); } return null; } public static boolean isIgnorable( final DataAttributes attributes, final DataAttributeContext context ) { if ( attributes == null ) { throw new NullPointerException(); } if ( context == null ) { throw new NullPointerException(); } final String source = (String) attributes.getMetaAttribute( MetaAttributeNames.Core.NAMESPACE, MetaAttributeNames.Core.SOURCE, String.class, context ); if ( MetaAttributeNames.Core.SOURCE_VALUE_ENVIRONMENT.equals( source ) ) { return true; } if ( MetaAttributeNames.Core.SOURCE_VALUE_EXPRESSION.equals( source ) ) { final Class expressionType = (Class) attributes.getMetaAttribute( MetaAttributeNames.Expressions.NAMESPACE, MetaAttributeNames.Expressions.CLASS, Class.class, context ); if ( expressionType == null ) { return false; } if ( ExpressionRegistry.getInstance().isExpressionRegistered( expressionType.getClass().getName() ) ) { final ExpressionMetaData data = ExpressionRegistry.getInstance().getExpressionMetaData( expressionType.getName() ); if ( data.isElementLayoutProcessor() || data.isGlobalLayoutProcessor() ) { // ignore the expression .. return true; } } return false; } if ( MetaAttributeNames.Core.SOURCE_VALUE_PARAMETER.equals( source ) ) { final Boolean include = (Boolean) attributes.getMetaAttribute( MetaAttributeNames.Parameters.NAMESPACE, MetaAttributeNames.Parameters.INCLUDE_IN_WIZARD, Boolean.class, context ); if ( Boolean.TRUE.equals( include ) ) { return false; } return true; } final Object indexColumn = attributes.getMetaAttribute( MetaAttributeNames.Core.NAMESPACE, MetaAttributeNames.Core.INDEXED_COLUMN, Boolean.class, context ); if ( Boolean.TRUE.equals( indexColumn ) ) { return true; } return false; } public static Band findGeneratedContent( final Band band ) { final Band generatedContentInternal = findGeneratedContentInternal( band ); if ( generatedContentInternal != null ) { generatedContentInternal.clear(); return generatedContentInternal; } if ( band.getElementCount() == 0 ) { return band; } return null; } private static Band findGeneratedContentInternal( final Band band ) { if ( Boolean.TRUE.equals( band.getAttribute( AttributeNames.Wizard.NAMESPACE, AttributeNames.Wizard.GENERATED_CONTENT_MARKER ) ) ) { return band; } final Element[] elements = band.getElementArray(); for ( int i = 0; i < elements.length; i++ ) { final Element element = elements[i]; if ( element instanceof Band ) { final Band retval = findGeneratedContentInternal( (Band) element ); if ( retval != null ) { return retval; } } } return null; } }