/* * 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.designer.relational.model; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Properties; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.osgi.util.NLS; import org.teiid.core.designer.HashCodeUtil; import org.teiid.core.designer.util.CoreStringUtil; import org.teiid.core.designer.util.StringUtilities; import org.teiid.designer.metamodels.relational.aspects.validation.RelationalStringNameValidator; import org.teiid.designer.relational.Messages; import org.teiid.designer.relational.RelationalPlugin; /** * * * @since 8.0 */ public class RelationalProcedure extends RelationalReference { public enum PROCEDURE_TYPE { PROCEDURE, FUNCTION, SOURCE_FUNCTION, NATIVE_QUERY_PROCEDURE } /** * */ public static final String KEY_FUNCTION = "FUNCTION"; //$NON-NLS-1$ /** * */ public static final String KEY_UPDATE_COUNT = "UPDATECOUNT"; //$NON-NLS-1$ /** * */ public static final boolean DEFAULT_FUNCTION = false; /** * */ public static final String DEFAULT_UPDATE_COUNT = "AUTO"; //$NON-NLS-1$ /** * */ public static final String DEFAULT_DATATYPE = "string"; //$NON-NLS-1$ private boolean nonPrepared = false; private boolean deterministic = false; private boolean returnsNullOnNull = false; private boolean variableArguments = false; private boolean aggregate = false; private boolean allowsDistinct = false; private boolean allowsOrderBy = false; private boolean analytic = false; private boolean decomposable = false; private boolean useDistinctRows = false; private String nativeQuery; private String javaClass; private String javaMethod; private String udfJarPath; private String functionCategory; boolean nativeQueryProcedure; PROCEDURE_TYPE procedureType; private String updateCount; private List<RelationalParameter> parameters; private RelationalProcedureResultSet resultSet; /** * RelationalProcedure constructor */ public RelationalProcedure() { super(); setType(TYPES.PROCEDURE); this.parameters = new ArrayList<RelationalParameter>(); setNameValidator(new RelationalStringNameValidator(true, true)); } /** * RelationalProcedure constructor * @param name the procedure name */ public RelationalProcedure( String name ) { super(name); setType(TYPES.PROCEDURE); this.parameters = new ArrayList<RelationalParameter>(); setNameValidator(new RelationalStringNameValidator(true, true)); } /** * @return updateCount */ public String getUpdateCount() { return updateCount; } /** * @param updateCount Sets updateCount to the specified value. */ public void setUpdateCount( String updateCount ) { this.updateCount = updateCount; } /** * @return function */ public boolean isNonPrepared() { return nonPrepared; } /** * @param nonPrepared Sets non-prepared to the specified value. */ public void setNonPrepared( boolean nonPrepared ) { this.nonPrepared = nonPrepared; } /** * @return function */ public boolean isFunction() { return this.procedureType == PROCEDURE_TYPE.FUNCTION || this.procedureType == PROCEDURE_TYPE.SOURCE_FUNCTION; } /** * @param function Sets function to the specified value. */ public void setFunction( boolean function ) { if( function ) { setProcedureType(PROCEDURE_TYPE.FUNCTION); } else { setProcedureType(PROCEDURE_TYPE.PROCEDURE); } } /** * @return deterministic */ public boolean isDeterministic() { return deterministic; } /** * @param deterministic Sets deterministic to the specified value. */ public void setDeterministic( boolean deterministic ) { this.deterministic = deterministic; } /** * @return returnsNullOnNull */ public boolean isReturnsNullOnNull() { return returnsNullOnNull; } /** * @param returnsNullOnNull Sets returnsNullOnNull to the specified value. */ public void setReturnsNullOnNull( boolean returnsNullOnNull ) { this.returnsNullOnNull = returnsNullOnNull; } /** * @return variableArguments */ public boolean isVariableArguments() { return variableArguments; } /** * @param variableArguments Sets variableArguments to the specified value. */ public void setVariableArguments( boolean variableArguments ) { this.variableArguments = variableArguments; } /** * @return aggregate */ public boolean isAggregate() { return aggregate; } /** * @param aggregate Sets aggregate to the specified value. */ public void setAggregate( boolean aggregate ) { this.aggregate = aggregate; } /** * @return allowsDistinct */ public boolean isAllowsDistinct() { return allowsDistinct; } /** * @param allowsDistinct Sets allowsDistinct to the specified value. */ public void setAllowsDistinct( boolean allowsDistinct ) { this.allowsDistinct = allowsDistinct; } /** * @return allowsOrderBy */ public boolean isAllowsOrderBy() { return allowsOrderBy; } /** * @param allowsOrderBy Sets allowsOrderBy to the specified value. */ public void setAllowsOrderBy( boolean allowsOrderBy ) { this.allowsOrderBy = allowsOrderBy; } /** * @return analytic */ public boolean isAnalytic() { return analytic; } /** * @param analytic Sets analytic to the specified value. */ public void setAnalytic( boolean analytic ) { this.analytic = analytic; } /** * @return decomposable */ public boolean isDecomposable() { return decomposable; } /** * @param decomposable Sets decomposable to the specified value. */ public void setDecomposable( boolean decomposable ) { this.decomposable = decomposable; } /** * @return useDistinctRows */ public boolean isUseDistinctRows() { return useDistinctRows; } /** * @param useDistinctRows Sets useDistinctRows to the specified value. */ public void setUseDistinctRows( boolean useDistinctRows ) { this.useDistinctRows = useDistinctRows; } /** * @return is source function */ public boolean isSourceFunction() { return this.procedureType == PROCEDURE_TYPE.SOURCE_FUNCTION; } /** * @return is native query procedure */ public boolean isNativeQueryProcedure() { return this.procedureType == PROCEDURE_TYPE.NATIVE_QUERY_PROCEDURE; } /** * */ public PROCEDURE_TYPE getProcedureType() { return procedureType; } /** * */ public void setProcedureType(PROCEDURE_TYPE type) { this.procedureType = type; } /** * @return java class name for function may be null */ public String getJavaClassName() { return javaClass; } /** * @param javaClassName sets java class name to the specified value. may be null */ public void setJavaClassName( String javaClassName ) { if( StringUtilities.areDifferent(this.javaClass, javaClassName) ) { this.javaClass = javaClassName; handleInfoChanged(); } } /** * @return java class name for function may be null */ public String getJavaMethodName() { return javaMethod; } /** * @param javaMethodName sets java method name to the specified value. may be null */ public void setJavaMethodName( String javaMethodName ) { if( StringUtilities.areDifferent(this.javaMethod, javaMethodName) ) { this.javaMethod = javaMethodName; handleInfoChanged(); } } /** * @return udf jar path */ public String getUdfJarPath() { return udfJarPath; } /** * @param udfJarPath sets relative udf jar path. may be null */ public void setUdfJarPath( String udfJarPath ) { if( StringUtilities.areDifferent(this.udfJarPath, udfJarPath) ) { this.udfJarPath = udfJarPath; handleInfoChanged(); } } /** * @return function category */ public String getFunctionCategory() { return functionCategory; } /** * @param category sets user defined function category. may be null */ public void setFunctionCategory( String category ) { if( StringUtilities.areDifferent(this.functionCategory, category) ) { this.functionCategory = category; handleInfoChanged(); } } /** * @return nativeQuery may be null */ public String getNativeQuery() { return nativeQuery; } /** * @param newQuery sets nativeQuery to the specified value. may be null */ public void setNativeQuery( String newQuery ) { if( StringUtilities.areDifferent(this.nativeQuery, newQuery) ) { this.nativeQuery = newQuery; handleInfoChanged(); } } /** * @return resultSet */ public RelationalProcedureResultSet getResultSet() { return resultSet; } /** * @param resultSet Sets resultSet to the specified value. */ public void setResultSet( RelationalProcedureResultSet resultSet ) { if( this.resultSet != null ) { this.resultSet.setParent(null); } this.resultSet = resultSet; if( this.resultSet != null) { this.resultSet.setParent(this); } } /** * @return parameters */ public List<RelationalParameter> getParameters() { return this.parameters; } /** * @param parameter the new parameter */ public void addParameter(RelationalParameter parameter) { if( this.parameters.add(parameter) ) { parameter.setParent(this); handleInfoChanged(); } } /** * @param parameter the parameter to remove * @return if parameter was removed or not */ public boolean removeParameter(RelationalParameter parameter) { if( this.parameters.remove(parameter) ) { handleInfoChanged(); return true; } return false; } /** * @return the new RelationalParameter */ public RelationalParameter createParameter() { return createParameter(DEFAULT_DATATYPE, RelationalParameter.DEFAULT_STRING_LENGTH); } /** * @param datatype the datatype name * @param length the datatype length * @return the new RelationalParameter */ public RelationalParameter createParameter(String datatype, int length) { return createParameter("newParameter_" + (getParameters().size() + 1), datatype, length); //$NON-NLS-1$ } /** * @param name the name of the parameter * @param datatype the datatype name * @param length the datatype length * @return the new RelationalParameter */ public RelationalParameter createParameter(String name, String datatype, int length) { RelationalParameter newParameter = new RelationalParameter(name); newParameter.setDatatype(datatype); newParameter.setLength(length); addParameter(newParameter); return newParameter; } /** * @param parameter the parameter * @return if parameter can be moved up in child list */ public boolean canMoveParameterUp(RelationalParameter parameter) { return getParameterIndex(parameter) > 0; } /** * @param parameter the parameter * @return if parameter can be moved down in child list */ public boolean canMoveParameterDown(RelationalParameter parameter) { return getParameterIndex(parameter) < getParameters().size()-1; } private int getParameterIndex(RelationalParameter parameter) { int i=0; for( RelationalParameter existingParameter : getParameters() ) { if( existingParameter == parameter) { return i; } i++; } // Shouldn't ever get here! return -1; } /** * @param theParameter the parameter */ public void moveParameterUp(RelationalParameter theParameter) { int startIndex = getParameterIndex(theParameter); if( startIndex > 0 ) { // Make Copy of List & get parameterInfo of startIndex-1 RelationalParameter[] existingParameters = getParameters().toArray(new RelationalParameter[0]); RelationalParameter priorParameter = existingParameters[startIndex-1]; existingParameters[startIndex-1] = theParameter; existingParameters[startIndex] = priorParameter; List<RelationalParameter> newParameters = new ArrayList<RelationalParameter>(existingParameters.length); for( RelationalParameter info : existingParameters) { newParameters.add(info); } this.parameters = newParameters; } } /** * @param theParameter the parameter */ public void moveParameterDown(RelationalParameter theParameter) { int startIndex = getParameterIndex(theParameter); if( startIndex < (getParameters().size()-1) ) { // Make Copy of List & get parameterInfo of startIndex+1 RelationalParameter[] existingParameters = getParameters().toArray(new RelationalParameter[0]); RelationalParameter afterParameter = existingParameters[startIndex+1]; existingParameters[startIndex+1] = theParameter; existingParameters[startIndex] = afterParameter; List<RelationalParameter> newParameters = new ArrayList<RelationalParameter>(existingParameters.length); for( RelationalParameter info : existingParameters) { newParameters.add(info); } this.parameters = newParameters; } } /** * @param props the properties */ public void setProperties(Properties props) { for( Object key : props.keySet() ) { String keyStr = (String)key; String value = props.getProperty(keyStr); if( value != null && value.length() == 0 ) { continue; } if( keyStr.equalsIgnoreCase(KEY_NAME) ) { setName(value); } else if(keyStr.equalsIgnoreCase(KEY_NAME_IN_SOURCE) ) { setNameInSource(value); } else if(keyStr.equalsIgnoreCase(KEY_DESCRIPTION) ) { setDescription(value); } else if(keyStr.equalsIgnoreCase(KEY_FUNCTION) ) { if( Boolean.parseBoolean(value) ) { setProcedureType(PROCEDURE_TYPE.FUNCTION); } } else if(keyStr.equalsIgnoreCase(KEY_UPDATE_COUNT) ) { setUpdateCount(value); } } } @Override public void handleInfoChanged() { super.handleInfoChanged(); // Set extension properties here?? if( this.isFunction() ) { if( this.functionCategory != null ) { getExtensionProperties().put(FUNCTION_CATEGORY, this.functionCategory ); } else getExtensionProperties().remove(FUNCTION_CATEGORY); if( this.javaClass != null ) { getExtensionProperties().put(JAVA_CLASS, this.javaClass ); } else getExtensionProperties().remove(JAVA_CLASS); if( this.javaMethod != null ) { getExtensionProperties().put(JAVA_METHOD, this.javaMethod ); } else getExtensionProperties().remove(JAVA_METHOD); if( this.udfJarPath != null ) { getExtensionProperties().put(UDF_JAR_PATH, this.udfJarPath ); } else getExtensionProperties().remove(UDF_JAR_PATH); getExtensionProperties().put(AGGREGATE, Boolean.toString(this.isAggregate()) ); getExtensionProperties().put(VARARGS, Boolean.toString(this.isVariableArguments()) ); getExtensionProperties().put(DETERMINISTIC, Boolean.toString(this.isDeterministic()) ); getExtensionProperties().put(NULL_ON_NULL, Boolean.toString(this.isReturnsNullOnNull()) ); // If aggregate == FALSE if( this.isAggregate() ) { getExtensionProperties().remove(ANALYTIC); getExtensionProperties().remove(ALLOWS_ORDER_BY); getExtensionProperties().remove(USES_DISTINCT_ROWS); getExtensionProperties().remove(DECOMPOSABLE); getExtensionProperties().remove(ALLOWS_DISTINCT); } else { getExtensionProperties().put(ANALYTIC, Boolean.toString(this.isAnalytic())); getExtensionProperties().put(ALLOWS_ORDER_BY, Boolean.toString(this.isAllowsOrderBy())); getExtensionProperties().put(USES_DISTINCT_ROWS, Boolean.toString(this.isUseDistinctRows())); getExtensionProperties().put(DECOMPOSABLE, Boolean.toString(this.isDecomposable())); getExtensionProperties().put(ALLOWS_DISTINCT, Boolean.toString(this.isAllowsDistinct())); } getExtensionProperties().remove(NATIVE_QUERY); } else { if( this.nativeQuery != null ) { getExtensionProperties().put(NATIVE_QUERY, this.nativeQuery ); } else getExtensionProperties().remove(NATIVE_QUERY); getExtensionProperties().put(NON_PREPARED, Boolean.toString(this.isNonPrepared())); // make sure model object does not have these extension properties for when function is false getExtensionProperties().remove(DETERMINISTIC); getExtensionProperties().remove(JAVA_CLASS); getExtensionProperties().remove(JAVA_METHOD); getExtensionProperties().remove(FUNCTION_CATEGORY); getExtensionProperties().remove(UDF_JAR_PATH); getExtensionProperties().remove(VARARGS); getExtensionProperties().remove(NULL_ON_NULL); getExtensionProperties().remove(AGGREGATE); getExtensionProperties().remove(ANALYTIC); getExtensionProperties().remove(ALLOWS_ORDER_BY); getExtensionProperties().remove(USES_DISTINCT_ROWS); getExtensionProperties().remove(DECOMPOSABLE); getExtensionProperties().remove(ALLOWS_DISTINCT); } } @Override public void validate() { // Walk through the properties for the table and set the status super.validate(); // Validate Children for( RelationalParameter param : getParameters() ) { param.validate(); } if( getStatus().getSeverity() == IStatus.ERROR ) { return; } // Check Column Status values for( RelationalParameter param : getParameters() ) { if( param.getStatus().getSeverity() == IStatus.ERROR ) { setStatus(new Status(IStatus.ERROR, RelationalPlugin.PLUGIN_ID, param.getStatus().getMessage() )); return; } } // Check Column Status values for( RelationalParameter outerParam : getParameters() ) { for( RelationalParameter innerParam : getParameters() ) { if( outerParam != innerParam ) { if( outerParam.getName().equalsIgnoreCase(innerParam.getName())) { setStatus(new Status(IStatus.ERROR, RelationalPlugin.PLUGIN_ID, NLS.bind(Messages.validate_error_duplicateParameterNamesInProcedure, getName()))); return; } } } } if( this.getParameters().isEmpty() ) { setStatus(new Status(IStatus.WARNING, RelationalPlugin.PLUGIN_ID, Messages.validate_warning_noParametersDefined )); } // Check for more than one RETURN parameter if Function if( this.isFunction() ) { boolean foundResultParam = false; for( RelationalParameter param : getParameters() ) { if( param.getDirection().equalsIgnoreCase(DIRECTION.RETURN)) { if( foundResultParam ) { setStatus(new Status(IStatus.ERROR, RelationalPlugin.PLUGIN_ID, Messages.validate_error_tooManyResultParametersInFunction )); return; } else { foundResultParam = true; } } } if( this.isSourceFunction() ) { if( getResultSet() != null ) { setStatus(new Status(IStatus.ERROR, RelationalPlugin.PLUGIN_ID, Messages.validate_noResultSetAllowedInFunction )); return; } } else { // Check for null category, class or method name if( this.functionCategory == null || this.functionCategory.trim().length() == 0 ) { setStatus(new Status(IStatus.ERROR, RelationalPlugin.PLUGIN_ID, Messages.validate_categoryUndefinedForUDF )); return; } if( this.javaClass == null || this.javaClass.trim().length() == 0 ) { setStatus(new Status(IStatus.ERROR, RelationalPlugin.PLUGIN_ID, Messages.validate_javaClassUndefinedForUDF )); return; } if( this.javaMethod == null || this.javaMethod.trim().length() == 0 ) { setStatus(new Status(IStatus.ERROR, RelationalPlugin.PLUGIN_ID, Messages.validate_javaMethodUndefinedForUDF )); return; } } } else { if( getResultSet() != null ) { if( getResultSet().getStatus().getSeverity() == IStatus.ERROR ) { setStatus(new Status(IStatus.ERROR, RelationalPlugin.PLUGIN_ID, getResultSet().getStatus().getMessage() )); return; } if( getResultSet().getStatus().getSeverity() == IStatus.WARNING ) { setStatus(new Status(IStatus.WARNING, RelationalPlugin.PLUGIN_ID, getResultSet().getStatus().getMessage() )); return; } } } } /** * {@inheritDoc} * * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals( final Object object ) { if (!super.equals(object)) { return false; } if (this == object) return true; if (object == null) return false; if (getClass() != object.getClass()) return false; final RelationalProcedure other = (RelationalProcedure)object; // string properties if (!CoreStringUtil.valuesAreEqual(getNativeQuery(), other.getNativeQuery()) || !CoreStringUtil.valuesAreEqual(getFunctionCategory(), other.getFunctionCategory()) || !CoreStringUtil.valuesAreEqual(getJavaClassName(), other.getJavaClassName()) || !CoreStringUtil.valuesAreEqual(getJavaMethodName(), other.getJavaMethodName()) || !CoreStringUtil.valuesAreEqual(getUdfJarPath(), other.getUdfJarPath()) || !CoreStringUtil.valuesAreEqual(getUpdateCount(), other.getUpdateCount()) ) { return false; } if( !(isAggregate()==other.isAggregate()) || !(isAllowsDistinct()==other.isAllowsDistinct()) || !(isAllowsOrderBy()==other.isAllowsOrderBy()) || !(isAnalytic()==other.isAnalytic()) || !(isDecomposable()==other.isDecomposable()) || !(isDeterministic()==other.isDeterministic()) || !(isFunction()==other.isFunction()) || !(isNonPrepared()==other.isNonPrepared()) || !(isReturnsNullOnNull()==other.isReturnsNullOnNull()) || !(isSourceFunction()==other.isSourceFunction()) || !(isUseDistinctRows()==other.isUseDistinctRows()) || !(isVariableArguments()==other.isVariableArguments()) ) { return false; } // ResultSet if (resultSet == null) { if (other.resultSet != null) return false; } else if (!resultSet.equals(other.resultSet)) return false; // Parameters Collection<RelationalParameter> thisParameters = getParameters(); Collection<RelationalParameter> thatParameters = other.getParameters(); if (thisParameters.size() != thatParameters.size()) { return false; } if (!thisParameters.isEmpty() && !thisParameters.equals(thatParameters)) { return false; } return true; } /** * {@inheritDoc} * * @see java.lang.Object#hashCode() */ @Override public int hashCode() { int result = super.hashCode(); // string properties if (!CoreStringUtil.isEmpty(getNativeQuery())) { result = HashCodeUtil.hashCode(result, getNativeQuery()); } if (!CoreStringUtil.isEmpty(getFunctionCategory())) { result = HashCodeUtil.hashCode(result, getFunctionCategory()); } if (!CoreStringUtil.isEmpty(getJavaClassName())) { result = HashCodeUtil.hashCode(result, getJavaClassName()); } if (!CoreStringUtil.isEmpty(getJavaMethodName())) { result = HashCodeUtil.hashCode(result, getJavaMethodName()); } if (!CoreStringUtil.isEmpty(getUdfJarPath())) { result = HashCodeUtil.hashCode(result, getUdfJarPath()); } if (!CoreStringUtil.isEmpty(getUpdateCount())) { result = HashCodeUtil.hashCode(result, getUpdateCount()); } result = HashCodeUtil.hashCode(result, isAggregate()); result = HashCodeUtil.hashCode(result, isAllowsDistinct()); result = HashCodeUtil.hashCode(result, isAllowsOrderBy()); result = HashCodeUtil.hashCode(result, isAnalytic()); result = HashCodeUtil.hashCode(result, isDecomposable()); result = HashCodeUtil.hashCode(result, isDeterministic()); result = HashCodeUtil.hashCode(result, isFunction()); result = HashCodeUtil.hashCode(result, isNonPrepared()); result = HashCodeUtil.hashCode(result, isReturnsNullOnNull()); result = HashCodeUtil.hashCode(result, isSourceFunction()); result = HashCodeUtil.hashCode(result, isUseDistinctRows()); result = HashCodeUtil.hashCode(result, isVariableArguments()); if(resultSet!=null) { result = HashCodeUtil.hashCode(result, resultSet); } List<RelationalParameter> params = getParameters(); for(RelationalParameter param: params) { result = HashCodeUtil.hashCode(result, param); } return result; } }