/*
* 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 - 2009 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.ImageContainer;
import org.pentaho.reporting.engine.classic.core.MetaAttributeNames;
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;
/**
* Todo: Document Me
*
* @author Thomas Morgner
*/
public class AutoGeneratorUtility
{
private AutoGeneratorUtility()
{
}
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 ElementType elementType;
final Class type = (Class) attributes.getMetaAttribute
(MetaAttributeNames.Core.NAMESPACE, MetaAttributeNames.Core.TYPE, Class.class, context);
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;
}
}