/*
* 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.pentaho.reporting.engine.classic.core.function.AbstractExpression;
import org.pentaho.reporting.engine.classic.core.function.Expression;
import java.util.ArrayList;
import java.util.Arrays;
/**
* Maps a string read from a column into an other string. The possible mappings are given as (key, text) pairs. If the
* string from the column is null or matches none of the defined keys, a fallback value is returned.
* <p/>
* If the fallback value is undefined, the original value is returned instead.
*
* @author Thomas Morgner
*/
public class MapStringExpression extends AbstractExpression {
/**
* The field from where to read the key value.
*/
private String field;
/**
* The list of possible keys.
*/
private ArrayList keys;
/**
* The list of mapped values.
*/
private ArrayList values;
/**
* A flag defining, whether the key-lookup should be case-insensitive.
*/
private boolean ignoreCase;
/**
* The fallback value is returned if none of the defined keys matches.
*/
private String fallbackValue;
/**
* The null-value is returned if the key-field evaluates to <code>null</code>.
*/
private String nullValue;
/**
* Default Constructor.
*/
public MapStringExpression() {
keys = new ArrayList();
values = new ArrayList();
}
/**
* Returns the name of the field from where to read the key value.
*
* @return the field name.
*/
public String getField() {
return field;
}
/**
* Defines the name of the field from where to read the key value.
*
* @param field
* the field name.
*/
public void setField( final String field ) {
this.field = field;
}
/**
* Returns the value that is returned if the key-field evaluates to <code>null</code>.
*
* @return the null-value.
*/
public String getNullValue() {
return nullValue;
}
/**
* Defines the value that is returned if the key-field evaluates to <code>null</code>.
*
* @param nullValue
* the null-value.
*/
public void setNullValue( final String nullValue ) {
this.nullValue = nullValue;
}
/**
* Returns the value that is returned if none of the predefined keys matches the lookup-value.
*
* @return the fallback value.
*/
public String getFallbackValue() {
return fallbackValue;
}
/**
* Defines the value that is returned if none of the predefined keys matches the lookup-value.
*
* @param fallbackValue
* the fallback value.
*/
public void setFallbackValue( final String fallbackValue ) {
this.fallbackValue = fallbackValue;
}
/**
* Defines a key value to which the lookup-field's value is compared. If the key is defined, a matching value must be
* defined too.
*
* @param index
* the index position of the key in the list.
* @param key
* the key value.
*/
public void setKey( final int index, final String key ) {
if ( keys.size() == index ) {
keys.add( key );
} else {
keys.set( index, key );
}
}
/**
* Returns a key value at the given index.
*
* @param index
* the index position of the key in the list.
* @return the key value.
*/
public String getKey( final int index ) {
return (String) keys.get( index );
}
/**
* Returns the number of keys defined in the expression.
*
* @return the number of keys.
*/
public int getKeyCount() {
return keys.size();
}
/**
* Returns all defined keys as string array.
*
* @return all defined keys.
*/
public String[] getKey() {
return (String[]) keys.toArray( new String[keys.size()] );
}
/**
* Defines all keys using the values from the string array.
*
* @param keys
* all defined keys.
*/
public void setKey( final String[] keys ) {
this.keys.clear();
this.keys.addAll( Arrays.asList( keys ) );
}
/**
* Defines the mapped text for the key at the given position. This text is returned if the key matches the value read
* from the lookup-field column.
*
* @param index
* the index of the entry.
* @param value
* the text that is returned if the key is selected.
*/
public void setText( final int index, final String value ) {
if ( values.size() == index ) {
values.add( value );
} else {
values.set( index, value );
}
}
/**
* Returns the mapped text for the key at the given position. This text is returned if the key matches the value read
* from the lookup-field column.
*
* @param index
* the index of the entry.
* @return the text that is returned if the key is selected.
*/
public String getText( final int index ) {
return (String) values.get( index );
}
/**
* Returns the number of replacement text defined in this expression. This should match the number of keys defined.
*
* @return the number of texts defined.
*/
public int getTextCount() {
return values.size();
}
/**
* Returns all defined texts as string-array.
*
* @return all texts.
*/
public String[] getText() {
return (String[]) values.toArray( new String[values.size()] );
}
/**
* Defines all texts by using the values from the given text-array.
*
* @param texts
* the new text-values.
*/
public void setText( final String[] texts ) {
this.values.clear();
this.values.addAll( Arrays.asList( texts ) );
}
/**
* Returns, whether the key-lookup should be case-insensitive.
*
* @return true, if the key comparison is case-insensitive, false otherwise.
*/
public boolean isIgnoreCase() {
return ignoreCase;
}
/**
* Defines, whether the key-lookup should be case-insensitive.
*
* @param ignoreCase
* true, if the key comparison is case-insensitive, false otherwise.
*/
public void setIgnoreCase( final boolean ignoreCase ) {
this.ignoreCase = ignoreCase;
}
/**
* 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 MapStringExpression co = (MapStringExpression) super.getInstance();
co.values = (ArrayList) values.clone();
co.keys = (ArrayList) keys.clone();
return co;
}
/**
* Performs the lookup by first querying the given field, and then returning the defined text for the key-position.
*
* @return the value of the function.
*/
public Object getValue() {
final Object raw = getDataRow().get( getField() );
if ( raw == null ) {
return getNullValue();
}
final String text = String.valueOf( raw );
final int length = Math.min( keys.size(), values.size() );
for ( int i = 0; i < length; i++ ) {
final String key = (String) keys.get( i );
if ( isIgnoreCase() ) {
if ( text.equalsIgnoreCase( key ) ) {
return values.get( i );
}
} else {
if ( text.equals( key ) ) {
return values.get( i );
}
}
}
final String fallbackValue = getFallbackValue();
if ( fallbackValue != null ) {
return fallbackValue;
}
return raw;
}
}