/*
* JBoss, Home of Professional Open Source.
*
* See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing.
*
* See the AUTHORS.txt file distributed with this work for a full listing of individual contributors.
*/
package org.teiid.query.ui.builder.model;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.teiid.core.designer.util.CoreArgCheck;
import org.teiid.core.designer.util.I18nUtil;
import org.teiid.designer.core.ModelerCore;
import org.teiid.designer.query.IQueryFactory;
import org.teiid.designer.query.IQueryService;
import org.teiid.designer.query.sql.lang.IExpression;
import org.teiid.designer.query.sql.lang.ILanguageObject;
import org.teiid.designer.query.sql.symbol.IConstant;
import org.teiid.designer.query.sql.symbol.IFunction;
import org.teiid.designer.udf.IFunctionForm;
import org.teiid.designer.udf.IFunctionLibrary;
import org.teiid.designer.udf.UdfManager;
import org.teiid.query.ui.builder.util.BuilderUtils;
/**
* FunctionEditorModel
*
* @since 8.0
*/
public class FunctionEditorModel extends AbstractLanguageObjectEditorModel {
// /////////////////////////////////////////////////////////////////////////////////////////////
// CONSTANTS
// /////////////////////////////////////////////////////////////////////////////////////////////
private static final String PREFIX = I18nUtil.getPropertyPrefix(FunctionEditorModel.class);
private static final String NONE = Util.getString(PREFIX + "none"); //$NON-NLS-1$
public static final String CATEGORY = "CATEGORY"; //$NON-NLS-1$
public static final String SELECTED_FUNCTION = "SELECTED_FUNCTION"; //$NON-NLS-1$
// /////////////////////////////////////////////////////////////////////////////////////////////
// FIELDS
// /////////////////////////////////////////////////////////////////////////////////////////////
private List argNames; // the current function's argument names
private List argValues; // the current function's argument values
private String[] categories;
private String category;
private String[] functions; // collection of function names valid for current category
private IFunctionForm[] functionForms;
private static String defaultCategory;
/** The function library. All information about functions is obtained from here. */
private IFunctionLibrary funcLib;
private IFunctionForm selectedFunctionForm;
// /////////////////////////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS
// /////////////////////////////////////////////////////////////////////////////////////////////
public FunctionEditorModel() {
super(IFunction.class);
getCategories();
}
// /////////////////////////////////////////////////////////////////////////////////////////////
// METHODS
// /////////////////////////////////////////////////////////////////////////////////////////////
/* (non-Javadoc)
* @see org.teiid.query.ui.builder.model.AbstractLanguageObjectEditorModel#clear()
*/
@Override
public void clear() {
argNames = null;
argValues = null;
selectedFunctionForm = null;
super.clear();
}
private IFunctionForm findFunctionForm( String theFunctionName ) {
IFunctionForm result = null;
if (functionForms != null) {
for (int i = 0; i < functionForms.length; i++) {
if (functionForms[i].getDisplayString().equals(theFunctionName)) {
result = functionForms[i];
break;
}
}
}
return result;
}
public String[] getCategories() {
// not sure if users can dynamically add categories.
// make this construct categories each time this method is called
categories = null;
funcLib = UdfManager.getInstance().getFunctionLibrary(); //new FunctionLibrary(SystemFunctionManager.getSystemFunctions(),
//new FunctionTree(new UDFSource(Collections.EMPTY_LIST)));
List list = funcLib.getFunctionCategories();
if ((list != null) && !list.isEmpty()) {
Object[] temp = list.toArray();
if ((temp != null) && (temp.length > 0)) {
categories = new String[temp.length];
for (int i = 0; i < categories.length; i++) {
categories[i] = temp[i].toString();
}
}
}
// no categories found. shouldn't happen.
if (categories == null) {
categories = new String[1];
categories[0] = NONE;
}
defaultCategory = categories[0]; // set default to first category
return categories;
}
public String getCategory() {
return category;
}
public String getDefaultCategory() {
return defaultCategory;
}
/**
* Gets the current value.
*
* @return the current <code>Function</code>
* @throws IllegalStateException if the current value is not complete
*/
public IFunction getFunction() {
return (IFunction)getLanguageObject();
}
/* (non-Javadoc)
* @see org.teiid.query.ui.builder.model.AbstractLanguageObjectEditorModel#getLanguageObject()
*/
@Override
public ILanguageObject getLanguageObject() {
// return null if not complete
if (!isComplete()) {
return null;
}
int numArgs = argValues.size();
List<IExpression> args = new ArrayList<IExpression>();
for (int i = 0; i < numArgs; i++) {
args.add((IExpression)argValues.get(i));
}
IQueryService service = ModelerCore.getTeiidQueryService();
IQueryFactory factory = service.createQueryFactory();
return factory.createFunction(selectedFunctionForm.getName(), args);
}
/**
* Gets the appropriate value for the function argument trying to use the given value.
*
* @param theFunctionName the function name
* @param theArgName the argument name
* @param theProposedValue the proposed value
*/
private Object getFunctionArgValue( String theFunctionName,
String theArgName,
Object theProposedValue ) {
Object result = theProposedValue;
if (BuilderUtils.isConversionTypeArg(theFunctionName, theArgName)) {
if ((theProposedValue instanceof IConstant) && BuilderUtils.isConversionTypeConstant(theProposedValue)) {
result = theProposedValue;
} else {
result = BuilderUtils.createConversionTypeConstant();
}
}
return result;
}
public String[] getFunctions() {
return functions;
}
public List getFunctionArgNames() {
return argNames;
}
public String getFunctionDescription() {
return (selectedFunctionForm == null) ? null : selectedFunctionForm.getDescription();
}
public String getFunctionName() {
return (selectedFunctionForm == null) ? null : selectedFunctionForm.getDisplayString();
}
public List getFunctionArgValues() {
return argValues;
}
/* (non-Javadoc)
* @see org.teiid.query.ui.builder.model.AbstractLanguageObjectEditorModel#isComplete()
*/
@Override
public boolean isComplete() {
return (selectedFunctionForm != null);
}
public boolean isValid() {
boolean result = isComplete();
if (result) {
// make sure argNames have values
for (int size = argValues.size(), i = 0; i < size; i++) {
if (argValues.get(i) == null) {
result = false;
break;
}
}
}
return result;
}
public void setCategory( String theCategory ) {
// only set category if not null and valid
if ((theCategory != null)) {
boolean changeCategory = true;
if (category == null ) {
changeCategory = true;
} else if((category.toUpperCase() != null) && category.equals(theCategory.toUpperCase()) ) {
changeCategory = false;
}
if (changeCategory) {
category = theCategory;
// get corresponding functions for category
functions = null;
functionForms = null;
selectedFunctionForm = null;
@SuppressWarnings("deprecation")
List<IFunctionForm> forms = funcLib.getFunctionForms(category);
if ((forms != null) && !forms.isEmpty()) {
// Create a SET of display strings to eliminate duplicate Method signatures
Set<String> filteredDisplayStrings = new HashSet<String>();
for( IFunctionForm iForm : forms ) {
String displayString = iForm.getDisplayString();
filteredDisplayStrings.add(displayString);
}
List<String> filteredList = new ArrayList<String>(filteredDisplayStrings);
// SORT the list
Collections.sort(filteredList);
// Set the functions list
functions = filteredList.toArray(new String[filteredList.size()]);
// Still create an array of IFunctionForms
int size = forms.size();
functionForms = new IFunctionForm[size];
for (int i = 0; i < size; i++) {
functionForms[i] = (IFunctionForm) forms.get(i);
}
} else {
functionForms = new IFunctionForm[1];
functions = new String[1];
functions[0] = NONE;
}
fireModelChanged(CATEGORY);
}
}
}
private void setFunction( IFunction theFunction ) {
notifyListeners = false;
if (theFunction == null) {
clear();
} else {
IExpression[] newArgValues = theFunction.getArgs();
IFunctionForm functionForm = funcLib.findFunctionForm(theFunction.getName(), newArgValues.length);
if( functionForm != null ) {
setCategory(functionForm.getCategory());
setFunctionName(functionForm.getDisplayString());
}
// set current arg values
argValues = Arrays.asList(newArgValues);
}
notifyListeners = true;
fireModelChanged(LanguageObjectEditorModelEvent.SAVED);
}
/**
* Sets the function argument at the given index.
*
* @param theValue
* @param theIndex
* @throws IllegalArgumentException if the argument value array is null
* @throws ArrayIndexOutOfBoundsException if the index is invalid
*/
public void setFunctionArgValue( IExpression theValue,
int theIndex ) {
CoreArgCheck.isNotNull(argValues);
argValues.set(theIndex, theValue);
}
public void setFunctionName( String theName ) {
IFunctionForm functionForm = findFunctionForm(theName);
CoreArgCheck.isNotNull(functionForm);
if ((selectedFunctionForm == null) || !selectedFunctionForm.equals(functionForm)) {
selectedFunctionForm = functionForm;
// set new function arguments
List prevArgValues = argValues;
int prevNumArgs = (prevArgValues == null) ? 0 : prevArgValues.size();
argNames = selectedFunctionForm.getArgNames();
argValues = new ArrayList(argNames.size());
// reuse prior arg values if possible
String functionName = selectedFunctionForm.getName();
for (int numArgs = argNames.size(), i = 0; i < numArgs; i++) {
// set value
String argName = (String)argNames.get(i);
Object value = null;
if (i < prevNumArgs) {
Object arg = prevArgValues.get(i);
if (!BuilderUtils.isConversionTypeConstant(arg) && !BuilderUtils.isConversionTypeArg(functionName, argName)) {
// keep value when both old/new argNames are not conversion type constants
value = getFunctionArgValue(functionName, argName, arg);
} else if (BuilderUtils.isConversionTypeConstant(arg)
&& BuilderUtils.isConversionTypeArg(functionName, argName)) {
// keep value when both old/new argNames are conversion type constants
value = getFunctionArgValue(functionName, argName, arg);
}
}
// if value is not set yet get undefined value or conversion type constant
if (value == null) {
value = getFunctionArgValue(functionName, argName, null);
}
argValues.add(value);
}
fireModelChanged(SELECTED_FUNCTION);
}
}
/* (non-Javadoc)
* @see org.teiid.query.ui.builder.model.AbstractLanguageObjectEditorModel#setLanguageObject(org.teiid.query.sql.LanguageObject)
*/
@Override
public void setLanguageObject( ILanguageObject theLangObj ) {
super.setLanguageObject(theLangObj);
setFunction((IFunction)theLangObj);
}
}