/* * 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.strings; 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.engine.classic.core.filter.MessageFormatSupport; import org.pentaho.reporting.engine.classic.core.function.AbstractExpression; import org.pentaho.reporting.engine.classic.core.function.Expression; import org.pentaho.reporting.libraries.base.util.URLEncoder; import java.io.UnsupportedEncodingException; import java.util.Date; /** * Formats a message using named parameters. The parameters are resolved against the current data-row. * <p/> * This performs the same task as the MessageFormatFilter does inside a text-element. * * @author Thomas Morgner */ public class MessageFormatExpression extends AbstractExpression { private static final Log logger = LogFactory.getLog( MessageFormatExpression.class ); /** * A internal data-row wrapper that URL-encodes all values returned by the data-row. */ private static class EncodeDataRow implements DataRow { /** * The wrappedDataRow datarow. */ private DataRow wrappedDataRow; /** * The character encoding used for the URL-encoding. */ private String encoding; /** * Default Constructor. */ protected EncodeDataRow() { } /** * Returns the wrapped data-row. * * @return the wrapped data-row. */ public DataRow getWrappedDataRow() { return wrappedDataRow; } /** * Defines the wrapped data-row. * * @param wrappedDataRow * the wrapped datarow. */ public void setWrappedDataRow( final DataRow wrappedDataRow ) { this.wrappedDataRow = wrappedDataRow; } /** * Returns the String-encoding used for the URL encoding. * * @return the string-encoding. */ public String getEncoding() { return encoding; } /** * Defines the String-encoding used for the URL encoding. * * @param encoding * the string-encoding. */ public void setEncoding( final String encoding ) { if ( encoding == null ) { throw new NullPointerException(); } this.encoding = encoding; } /** * Encodes the given value. The encoding process is skipped, if the value is null, is a number or is a date. * * @param fieldValue * the value that should be encoded. * @return the encoded value. */ private Object encode( final Object fieldValue ) { if ( fieldValue == null ) { return null; } if ( fieldValue instanceof Date ) { return fieldValue; } else if ( fieldValue instanceof Number ) { return fieldValue; } try { return URLEncoder.encode( String.valueOf( fieldValue ), encoding ); } catch ( UnsupportedEncodingException e ) { MessageFormatExpression.logger.debug( "Unsupported Encoding: " + encoding ); return null; } } /** * Returns the value of the function, expression or column using its specific name. The given name is translated * into a valid column number and the the column is queried. For functions and expressions, the * <code>getValue()</code> method is called and for columns from the tablemodel the tablemodel method * <code>getValueAt(row, column)</code> gets called. * * @param col * the item index. * @return the value. * @throws IllegalStateException * if the datarow detected a deadlock. */ public Object get( final String col ) throws IllegalStateException { return encode( wrappedDataRow.get( col ) ); } /** * Checks whether the value contained in the column has changed since the last advance-operation. * * @param name * the name of the column. * @return true, if the value has changed, false otherwise. */ public boolean isChanged( final String name ) { return wrappedDataRow.isChanged( name ); } public String[] getColumnNames() { return wrappedDataRow.getColumnNames(); } } /** * The message-format pattern used to compute the result. */ private String pattern; /** * The message format support translates raw message strings into useable MessageFormat parameters and read the * necessary input data from the datarow. */ private MessageFormatSupport messageFormatSupport; /** * 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; /** * Default constructor. */ public MessageFormatExpression() { messageFormatSupport = new MessageFormatSupport(); encoding = "UTF-8"; } /** * Returns the format string used in the message format. * * @return the format string. */ public String getPattern() { return pattern; } /** * Defines the format string for the {@link java.text.MessageFormat} object used in this implementation. * * @param pattern * the message format. */ public void setPattern( final String pattern ) { this.pattern = pattern; } /** * 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; } /** * Returns the replacement text that is used if one of the referenced message parameters is null. * * @return the replacement text for null-values. */ public String getNullString() { return messageFormatSupport.getNullString(); } /** * Defines the replacement text that is used if one of the referenced message parameters is null. * * @param nullString * the replacement text for null-values. */ public void setNullString( final String nullString ) { this.messageFormatSupport.setNullString( nullString ); } /** * Returns the formatted message. * * @return the formatted message. */ public Object getValue() { final ResourceBundleFactory resourceBundleFactory = getResourceBundleFactory(); messageFormatSupport.setFormatString( pattern ); messageFormatSupport.setLocale( resourceBundleFactory.getLocale() ); final String result; if ( isUrlEncodeValues() ) { final EncodeDataRow dataRow = new EncodeDataRow(); dataRow.setEncoding( encoding ); dataRow.setWrappedDataRow( getDataRow() ); result = messageFormatSupport.performFormat( dataRow ); } else { result = messageFormatSupport.performFormat( getDataRow() ); } if ( isUrlEncodeResult() ) { try { return URLEncoder.encode( result, getEncoding() ); } catch ( UnsupportedEncodingException e ) { MessageFormatExpression.logger.debug( "Unsupported Encoding: " + encoding ); return null; } } else { return result; } } public Expression getInstance() { final MessageFormatExpression ex = (MessageFormatExpression) super.getInstance(); ex.messageFormatSupport = new MessageFormatSupport(); return ex; } }