/* * 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.function; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.pentaho.reporting.engine.classic.core.DataRow; import org.pentaho.reporting.engine.classic.core.ResourceBundleFactory; import org.pentaho.reporting.libraries.base.util.ObjectUtilities; import org.pentaho.reporting.libraries.base.util.URLEncoder; import org.pentaho.reporting.libraries.formatting.FastMessageFormat; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.Locale; /** * A TextFormatExpression uses a java.text.MessageFormat to concat and format one or more values evaluated from an * expression, function or report datasource. * <p/> * The TextFormatExpression uses the pattern property to define the global format-pattern used when evaluating the * expression. The dataRow fields used to fill the expressions placeholders are defined in a list of properties where * the property-names are numbers. The property counting starts at "0". * <p/> * The Syntax of the <code>pattern</code> property is explained in the class {@link java.text.MessageFormat}. * <p/> * Example: * * <pre> * <expression name="expr" class="org.pentaho.reporting.engine.classic.core.function.TextFormatExpression"> * <properties> * <property name="pattern">Invoice for your order from {0, date, EEE, MMM d, * yyyy}</property> * <property name="fields[0]">printdate</property> * </properties> * </expression> * </pre> * <p/> * The {@link org.pentaho.reporting.engine.classic.core.function.strings.MessageFormatExpression} allows to specify * named field-references in the pattern, which greatly simplifies the pattern-definition. * * @author Thomas Morgner * @deprecated Use the MessageFormatExpression instead. */ @SuppressWarnings( "deprecation" ) public class TextFormatExpression extends AbstractExpression { private static final Log logger = LogFactory.getLog( TextFormatExpression.class ); /** * An ordered list containing the fieldnames used in the expression. */ private ArrayList fields; /** * A temporary array to reduce the number of object creations. */ private transient Object[] fieldValues; /** * A temporary array to reduce the number of object creations. This array is used to compute the cache's validity. */ private transient Object[] oldFieldValues; /** * The current locale. This is used to optimize the expression evaluation. */ private transient Locale locale; /** * The current message format object. */ private transient FastMessageFormat messageFormat; /** * The message-format pattern used to compute the result. */ private String pattern; /** * A flag indicating whether the data read from the fields should be URL encoded. */ private boolean urlEncodeData; /** * A flag indicating whether the whole result string should be URL encoded. */ private boolean urlEncodeResult; /** * The byte-encoding used for the URL encoding. */ private String encoding; /** * A cached result. */ private String cachedResult; /** * Default constructor, creates a new unnamed TextFormatExpression. */ public TextFormatExpression() { fields = new ArrayList(); encoding = "iso-8859-1"; } /** * Returns the defined character encoding that is used to transform the Java-Unicode strings into bytes. * * @return the encoding. */ public String getEncoding() { return encoding; } /** * Defines the character encoding that is used to transform the Java-Unicode strings into bytes. * * @param encoding * the encoding. */ public void setEncoding( final String encoding ) { if ( encoding == null ) { throw new NullPointerException(); } this.encoding = encoding; } /** * Defines, whether the values read from the data-row should be URL encoded. Dates and Number objects are never * encoded. * * @param urlEncode * true, if the values from the data-row should be URL encoded before they are passed to the MessageFormat, * false otherwise. */ public void setUrlEncodeValues( final boolean urlEncode ) { this.urlEncodeData = urlEncode; } /** * Queries, whether the values read from the data-row should be URL encoded. * * @return true, if the values are encoded, false otherwise. */ public boolean isUrlEncodeValues() { return urlEncodeData; } /** * Queries, whether the formatted result-string will be URL encoded. * * @return true, if the formatted result will be encoded, false otherwise. */ public boolean isUrlEncodeResult() { return urlEncodeResult; } /** * Defines, whether the formatted result-string will be URL encoded. * * @param urlEncodeResult * true, if the formatted result will be encoded, false otherwise. */ public void setUrlEncodeResult( final boolean urlEncodeResult ) { this.urlEncodeResult = urlEncodeResult; } /** * Evaluates the expression by collecting all values defined in the fieldlist from the datarow. The collected values * are then parsed and formated by the MessageFormat-object. * * @return a string containing the pattern inclusive the formatted values from the datarow */ public Object getValue() { if ( fields.isEmpty() ) { return getPattern(); } final ResourceBundleFactory factory = getResourceBundleFactory(); if ( messageFormat == null || ObjectUtilities.equal( locale, factory.getLocale() ) == false ) { messageFormat = new FastMessageFormat( getPattern(), factory.getLocale() ); this.locale = factory.getLocale(); } try { if ( oldFieldValues == null || oldFieldValues.length != fields.size() ) { oldFieldValues = new Object[fields.size()]; } else if ( fieldValues != null && fieldValues.length == oldFieldValues.length ) { System.arraycopy( fieldValues, 0, oldFieldValues, 0, fields.size() ); } fieldValues = getFieldValues( fieldValues ); final String result; if ( cachedResult != null && Arrays.equals( oldFieldValues, fieldValues ) ) { result = cachedResult; } else { result = messageFormat.format( fieldValues ); cachedResult = result; } if ( isUrlEncodeResult() ) { return URLEncoder.encode( result, getEncoding() ); } else { return result; } } catch ( UnsupportedEncodingException e ) { TextFormatExpression.logger.debug( "Unsupported Encoding: " + encoding ); return null; } } /** * Collects the values of all fields defined in the fieldList. * * @param retval * an optional array that will receive the field values. * @return an Object-array containing all defined values from the datarow * @throws java.io.UnsupportedEncodingException * if the character encoding is not recognized by the JDK. */ protected Object[] getFieldValues( Object[] retval ) throws UnsupportedEncodingException { final int size = fields.size(); if ( retval == null || retval.length != size ) { retval = new Object[size]; } final DataRow dataRow = getDataRow(); for ( int i = 0; i < size; i++ ) { final String field = (String) fields.get( i ); if ( field == null ) { retval[i] = null; continue; } final Object fieldValue = dataRow.get( field ); if ( isUrlEncodeValues() ) { if ( fieldValue == null ) { retval[i] = null; } else if ( fieldValue instanceof Date ) { retval[i] = fieldValue; } else if ( fieldValue instanceof Number ) { retval[i] = fieldValue; } else if ( isUrlEncodeValues() ) { retval[i] = URLEncoder.encode( String.valueOf( fieldValue ), encoding ); } else { retval[i] = fieldValue; } } else { retval[i] = fieldValue; } } return retval; } /** * Returns the pattern defined for this expression. * * @return the pattern. */ public String getPattern() { return pattern; } /** * Defines the pattern for this expression. The pattern syntax is defined by the java.text.MessageFormat object and * the given pattern string has to be valid according to the rules defined there. * * @param pattern * the pattern string */ public void setPattern( final String pattern ) { if ( pattern == null ) { throw new NullPointerException(); } this.messageFormat = null; this.pattern = pattern; this.cachedResult = null; } /** * Return a completly separated copy of this function. The copy does no longer share any changeable objects with the * original function. * * @return a copy of this function. */ public Expression getInstance() { final TextFormatExpression tex = (TextFormatExpression) super.getInstance(); tex.fields = (ArrayList) fields.clone(); tex.fieldValues = null; tex.oldFieldValues = null; tex.cachedResult = null; return tex; } /** * Defines the field in the field-list at the given index. * * @param index * the position in the list, where the field should be defined. * @param field * the name of the field. */ public void setField( final int index, final String field ) { if ( fields.size() == index ) { fields.add( field ); } else { fields.set( index, field ); } fieldValues = null; oldFieldValues = null; cachedResult = null; } /** * Returns the defined field at the given index-position. * * @param index * the position of the field name that should be queried. * @return the field name at the given position. */ public String getField( final int index ) { return (String) fields.get( index ); } /** * Returns the number of fields defined in this expression. * * @return the number of fields. */ public int getFieldCount() { return fields.size(); } /** * Returns all defined fields as array of strings. * * @return all the fields. */ public String[] getField() { return (String[]) fields.toArray( new String[fields.size()] ); } /** * Defines all fields as array. This completely replaces any previously defined fields. * * @param fields * the new list of fields. */ public void setField( final String[] fields ) { this.fields.clear(); this.fields.addAll( Arrays.asList( fields ) ); fieldValues = null; oldFieldValues = null; cachedResult = null; } }