/*
*
* 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 2005 - 2008 Pentaho Corporation. All rights reserved.
*
* @created Aug 31, 2005
* @author James Dixon
*/
package org.pentaho.platform.uifoundation.component;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
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;
/**
* 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
*
*/
abstract public 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);
}
*/
abstract protected 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$
} else {
// all is well with the world, you may proceed
}
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$
}
}
}