/* * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU General Public License, version 2 as published by the Free Software * Foundation. * * You should have received a copy of the GNU General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/gpl-2.0.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 General Public License for more details. * * * Copyright 2006 - 2013 Pentaho Corporation. All rights reserved. */ package org.pentaho.platform.uifoundation.component; import org.dom4j.Element; import org.pentaho.commons.connection.IPentahoMetaData; import org.pentaho.commons.connection.IPentahoResultSet; import org.pentaho.platform.api.engine.ComponentException; import org.pentaho.platform.api.engine.ILogger; import org.pentaho.platform.api.engine.IPentahoSession; import org.pentaho.platform.uifoundation.component.xml.FilterPanelException; import org.pentaho.platform.uifoundation.messages.Messages; import org.pentaho.platform.util.xml.XForm; import org.pentaho.platform.util.xml.dom4j.XmlDom4JHelper; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; /** * TODO sbarkdull, may be worth breaking into 4 separate classes, one for each of the ITEM_SOURCE_*'s of course * they would all implement the same interface or abstr class A factory class would look at the xml, and create the * appropriate instance/type * * @author unknow, probably James Dixon * */ public abstract class FilterDefinition { private static final String RE_HAS_WHITE_SPACE = ".+\\s+.+"; //$NON-NLS-1$ /** * the name of the title, identified by the element <title> in the filter panel definition file */ private String title; private String elementName; /** * a resultSet containing the text and values for the items to be displayed in the filter panel's control. The * result set may be a scoped variable, in which case it is retrieved by the key specified in the * <global-attribute> or <session-attribute> element in the filter panel definition file. Or, it is the output of * the action sequence, in which case it is retrieved from the action sequence's output specified by the name of * the <data-output> element of the filter panel definition file. */ private IPentahoResultSet resultSet; /** * * the type of the control in the filter panel, identified by the element <type> in the filter panel definition * file. Valid values are: radio,list,list-multi,check-multi,check-multi-scroll,check-multi-scroll-2-column, * check-multi-scroll-3-column,check-multi-scroll-4-column */ private int type; /** * name of the column to retrieve the names of the items placed in the filter panel's control related to the * member variable nameColumnNo */ protected String descriptionItem; /** * name of the column to retrieve the values of the items placed in the filter panel's control related to the * member variable valueColumnNo */ protected String valueItem; private String[] defaultValue; protected Element node; protected String formName; protected ILogger logger; protected IPentahoSession session; /** * index of the column to retrieve the names of the items placed in the filter panel's control related to the * member variable descriptionItem */ private int nameColumnNo = -1; /** * index of the column to retrieve the values of the items placed in the filter panel's control related to the * member variable valueItem */ private int valueColumnNo = -1; /** * Ctor, duh. * * @param node * @param formName * @param logger */ protected FilterDefinition( final Element node, final IPentahoSession session, final ILogger logger ) { this.logger = logger; this.node = node; this.session = session; } // public void setFormName(String formName) { // this.formName = formName; // } public String getTitle() { return title; } public String getName() { return elementName; } /** * order of precedence: session-attribute, global-attribute, data-solution, static-list * * @param xMLnode * @throws FilterPanelException */ public void fromXml( final Element xMLnode ) throws FilterPanelException { title = XmlDom4JHelper.getNodeText( "title", xMLnode ); //$NON-NLS-1$ elementName = XmlDom4JHelper.getNodeText( "name", xMLnode ); //$NON-NLS-1$ descriptionItem = XmlDom4JHelper.getNodeText( "data-display", xMLnode ); //$NON-NLS-1$ valueItem = XmlDom4JHelper.getNodeText( "data-value", xMLnode ); //$NON-NLS-1$ formName = XmlDom4JHelper.getNodeText( "name", xMLnode ); //$NON-NLS-1$ String typeStr = XmlDom4JHelper.getNodeText( "type", xMLnode ); //$NON-NLS-1$ if ( "radio".equalsIgnoreCase( typeStr ) ) { //$NON-NLS-1$ type = XForm.TYPE_RADIO; } else if ( "list".equalsIgnoreCase( typeStr ) ) { //$NON-NLS-1$ type = XForm.TYPE_LIST; } else if ( "list-multi".equalsIgnoreCase( typeStr ) ) { //$NON-NLS-1$ type = XForm.TYPE_LIST_MULTI; } else if ( "check-multi".equalsIgnoreCase( typeStr ) ) { //$NON-NLS-1$ type = XForm.TYPE_CHECK_MULTI; } else if ( "check-multi-scroll".equalsIgnoreCase( typeStr ) ) { //$NON-NLS-1$ type = XForm.TYPE_CHECK_MULTI_SCROLL; } else if ( "check-multi-scroll-2-column".equalsIgnoreCase( typeStr ) ) { //$NON-NLS-1$ type = XForm.TYPE_CHECK_MULTI_SCROLL_2_COLUMN; } else if ( "check-multi-scroll-3-column".equalsIgnoreCase( typeStr ) ) { //$NON-NLS-1$ type = XForm.TYPE_CHECK_MULTI_SCROLL_3_COLUMN; } else if ( "check-multi-scroll-4-column".equalsIgnoreCase( typeStr ) ) { //$NON-NLS-1$ type = XForm.TYPE_CHECK_MULTI_SCROLL_4_COLUMN; } else if ( "text-box".equalsIgnoreCase( typeStr ) ) { //$NON-NLS-1$ type = XForm.TYPE_TEXT; } else { type = XForm.TYPE_SELECT; } } public void setDefaultValue( final String[] defaultValue ) { this.defaultValue = defaultValue; } public boolean isValid( final String[] values ) { if ( values == null ) { return ( false ); } for ( String element : values ) { if ( !isValid( element ) ) { return ( false ); } } return ( true ); } protected boolean isValid( final String value ) { // this assume that the list of valid values does not have to be populated // This is a new field which let the user specify whether an empty value is a valide value or not String empty = XmlDom4JHelper.getNodeText( "empty-accepted", node ); //$NON-NLS-1$ // This is a new field which let the user specify if the value select has to be in the list or values or not String valueInList = XmlDom4JHelper.getNodeText( "value-in-list", node ); //$NON-NLS-1$ boolean isEmptyAccepted = Boolean.parseBoolean( empty ); boolean mustExistInList = ( ( valueInList != null ) && ( valueInList.length() > 0 ) ) ? Boolean.parseBoolean( valueInList ) : true; if ( mustExistInList ) { if ( value == null ) { return false; } if ( resultSet == null ) { // we cannot determin the values for this filter return false; } Object[] row = resultSet.next(); String rowValue; while ( row != null ) { rowValue = row[valueColumnNo].toString(); if ( value.equals( rowValue ) ) { resultSet.close(); return true; } row = resultSet.next(); } // close the result set so we can loop through it again later if we need to resultSet.close(); return false; } else { if ( isEmptyAccepted ) { return true; } else { return ( ( value != null ) && ( value.length() > 0 ) ); } } } // sbarkdull: doesn't appear to be used anywhere, temporarily removing it from the public interface /* * private boolean isValid(String[] value, Map parameterProviders) { if (resultSet == null && parameterProviders * != null) { populate(parameterProviders, value); } return isValid(value); * * } */ protected abstract IPentahoResultSet getResultSet( Map parameterProviders ); public boolean populate( final Map parameterProviders, final String[] value ) { // TODO apply session-based security // TODO support static lists of values defaultValue = value; resultSet = getResultSet( parameterProviders ); if ( resultSet != null ) { // find the column that we have been told to you IPentahoMetaData metaData = resultSet.getMetaData(); nameColumnNo = metaData.getColumnIndex( descriptionItem ); valueColumnNo = metaData.getColumnIndex( valueItem ); } return ( resultSet != null ); } /** * Create the XForm header and XForm body, and place the results in the parameters xformHeader and xformBody. * * @param xformHeader * StringBuffer containing the XForm header * @param xformBody * StringBuffer containing the XForm body * * @throws ComponentException * if this.nameColumnNo is -1, this.valueColumnNo is -1, or the this.resultSet is null. nameColumnNo is * likely to be -1 if this.descriptionItem does not correlate with the value of the <data-display> * element in the filter panel definition file and the name of a column in the resultSet. valueColumnNo * is likely to be -1 if this.valueItem does not correlate with the value of the <data-value> element * in the filter panel definition file and the name of a column in the resultSet. resultSet is likely * to be null if a result-set was not placed in session or global scope under the key identified by the * <global-attribute> or <session-attribute> element in the filter panel definition file, or if the * action sequence identified by <data-action> element in the filter panel definition file failed to * return a result set */ public void getXForm( final StringBuffer xformHeader, final StringBuffer xformBody ) throws ComponentException { // iterate thru the values to get the items and the display names String value; String name; HashMap displayNames = null; ArrayList items = null; // TODO support multiple column headers / row headers // TODO support an iteration across columns for a given row // If the result is non-empty then we will extract the values from the result set and display to the user if ( ( resultSet != null ) && ( resultSet.getRowCount() > 0 ) ) { displayNames = new HashMap(); items = new ArrayList(); if ( nameColumnNo == -1 ) { // we did not find the specified name column throw new ComponentException( Messages.getInstance().getErrorString( "FilterDefinition.ERROR_0001_NAME_COLUMN_MISSING", descriptionItem ) ); //$NON-NLS-1$ } else if ( valueColumnNo == -1 ) { // we did not find the specified name column throw new ComponentException( Messages.getInstance().getErrorString( "FilterDefinition.ERROR_0002_VALUE_COLUMN_MISSING", valueItem ) ); //$NON-NLS-1$ } Object[] row = null; try { row = resultSet.next(); } catch ( Exception e ) { // We will check for null below } if ( row == null ) { logger.warn( Messages.getInstance().getErrorString( "FilterDefinition.ERROR_0004_FILTER_DEFINITION_EMPTY" ) ); //$NON-NLS-1$ } else { while ( row != null ) { value = row[valueColumnNo].toString(); items.add( value ); name = row[nameColumnNo].toString(); if ( name != null ) { displayNames.put( value, name ); } row = resultSet.next(); } } // close the result set so we can loop through it again later if we need // to resultSet.close(); // now create the XForm for the item if ( displayNames.size() == 0 ) { displayNames = null; } } else { // ResultSet is null and it is ok only for a filter type of TEXT_BOX if ( type != XForm.TYPE_TEXT ) { throw new ComponentException( Messages.getInstance().getErrorString( "FilterDefinition.ERROR_0003_FILTER_DEFINITION_NULL" ) ); //$NON-NLS-1$ } } assert formName != null : Messages.getInstance().getErrorString( "FilterDefinition.ERROR_0005_NAME_ELEMENT_EMPTY" ); //$NON-NLS-1$ assert !formName.matches( FilterDefinition.RE_HAS_WHITE_SPACE ) : Messages.getInstance().getErrorString( "FilterDefinition.ERROR_0006_NAME_ELEMENT_WHITESPACE" ); //$NON-NLS-1$ XForm.createXFormControl( type, elementName, defaultValue, items, displayNames, formName, xformHeader, xformBody ); } public static void main( final String[] args ) { String[] nm = { " xx", "xx ", " xx ", "x x", "xx", "custome rnumber", " x x " }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ for ( String element : nm ) { boolean matches = element.matches( FilterDefinition.RE_HAS_WHITE_SPACE ); System.out.println( "[" + element + "] matches: " + matches ); //$NON-NLS-1$ //$NON-NLS-2$ } } }