/*
* 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.metamodels.relational.aspects.validation.rules;
import static org.teiid.designer.metamodels.relational.extension.RelationalModelExtensionConstants.NAMESPACE_PROVIDER;
import java.util.List;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.emf.ecore.EObject;
import org.teiid.core.designer.util.CoreArgCheck;
import org.teiid.core.designer.util.CoreStringUtil;
import org.teiid.designer.core.ModelerCore;
import org.teiid.designer.core.metamodel.aspect.AspectManager;
import org.teiid.designer.core.metamodel.aspect.sql.SqlProcedureAspect;
import org.teiid.designer.core.util.VdbHelper.VdbFolders;
import org.teiid.designer.core.validation.ObjectValidationRule;
import org.teiid.designer.core.validation.ValidationContext;
import org.teiid.designer.core.validation.ValidationProblem;
import org.teiid.designer.core.validation.ValidationProblemImpl;
import org.teiid.designer.core.validation.ValidationResult;
import org.teiid.designer.core.validation.ValidationResultImpl;
import org.teiid.designer.core.workspace.ModelResource;
import org.teiid.designer.extension.ExtensionPlugin;
import org.teiid.designer.extension.definition.ModelObjectExtensionAssistant;
import org.teiid.designer.metamodels.relational.DirectionKind;
import org.teiid.designer.metamodels.relational.Procedure;
import org.teiid.designer.metamodels.relational.ProcedureParameter;
import org.teiid.designer.metamodels.relational.RelationalPlugin;
import org.teiid.designer.metamodels.relational.extension.RelationalModelExtensionConstants;
/**
* ProcedureFunctionRule
* Validation Rules for Relational Procedures where function=true
* -- view procedures - pushdown is determined automatically
* -- source procedures - if no "java-method" property "PUSHDOWN_REQUIRED"
*
* @since 8.1
*/
public class ProcedureFunctionRule implements ObjectValidationRule {
/*
* @See org.teiid.designer.core.validation.ObjectValidationRule#validate(org.eclipse.emf.ecore.EObject, org.teiid.designer.core.validation.ValidationContext)
*/
@Override
public void validate(EObject eObject, ValidationContext context) {
CoreArgCheck.isInstanceOf(Procedure.class, eObject);
Procedure procedure = (Procedure) eObject;
SqlProcedureAspect procAspect = (SqlProcedureAspect) AspectManager.getSqlAspect(eObject);
// If procedure is not a virtual function, return.
if(!procedure.isFunction()) return;
// For virtual procedure functions, all 'Udf' properties must be validated
// For physical procedure functions, only validate the 'Udf' properties if either 'java-class' or 'java-method' is not empty.
boolean isVirtual = procAspect.isVirtual(eObject);
// If not virtual, need to check java-method and java-class properties
String javaClass = getJavaClass(procedure);
String javaMethod = getJavaMethod(procedure);
// If both empty, no need to validate
if( !isVirtual && (javaClass==null || javaClass.trim().isEmpty()) && (javaMethod==null || javaMethod.trim().isEmpty()) ) {
return;
}
ValidationResult result = new ValidationResultImpl(eObject);
// validate the return parameter
validateReturnParameter(procedure, result);
// validate java-class invocation class
if(CoreStringUtil.isEmpty(javaClass)) {
ValidationProblem problem = new ValidationProblemImpl(0, IStatus.ERROR ,RelationalPlugin.Util.getString("ProcedureFunctionRule.javaClassNotSpecified")); //$NON-NLS-1$
result.addProblem(problem);
} else {
validateJavaIdentifier(javaClass, RelationalPlugin.Util.getString("ProcedureFunctionRule.javaClass",javaClass), true, result); //$NON-NLS-1$
}
// validate invocation method
if(CoreStringUtil.isEmpty(javaMethod)) {
ValidationProblem problem = new ValidationProblemImpl(0, IStatus.ERROR ,RelationalPlugin.Util.getString("ProcedureFunctionRule.javaMethodNotSpecified")); //$NON-NLS-1$
result.addProblem(problem);
} else {
validateJavaIdentifier(javaMethod, RelationalPlugin.Util.getString("ProcedureFunctionRule.javaMethod",javaMethod), false, result); //$NON-NLS-1$
}
// validate jarPath property
validateUdfJarPath(procedure,result);
// validate function category
String category = getFunctionCategory(procedure);
if (CoreStringUtil.isEmpty(category)) {
ValidationProblem problem = new ValidationProblemImpl(0, IStatus.ERROR ,RelationalPlugin.Util.getString("ProcedureFunctionRule.categoryNotSpecified")); //$NON-NLS-1$
result.addProblem(problem);
}
// add the result to the context
context.addResult(result);
}
/**
* Validate the udfJarPath property.
* - the path must be set
* - the specified jar must be located in the workspace project
* @param scalarFunc the Scalar Function to validate
* @param result the ValidationResult
*/
private final void validateUdfJarPath(Procedure proc, ValidationResult result) {
String udfJarPath = getUdfJarPath(proc);
if(udfJarPath!=null) {
if (CoreStringUtil.isEmpty(udfJarPath.trim())) {
String message = RelationalPlugin.Util.getString("ProcedureFunctionRule.udfJarPathNotSet"); //$NON-NLS-1$
ValidationProblem problem = new ValidationProblemImpl(0, IStatus.ERROR ,message);
result.addProblem(problem);
} else {
final ModelResource resrc = ModelerCore.getModelWorkspace().findModelResource(proc);
IProject project = resrc.getModelProject().getProject();
IFolder libFolder = getUdfJarFolder(project);
boolean found = isJarInFolder(libFolder,udfJarPath);
if(!found) {
String message = RelationalPlugin.Util.getString("ProcedureFunctionRule.udfJarNotFound",udfJarPath); //$NON-NLS-1$
ValidationProblem problem = new ValidationProblemImpl(0, IStatus.ERROR ,message);
result.addProblem(problem);
}
}
}
}
/**
* Get the java-class property from the supplied Procedure
* @param proc the supplied Procedure
* @return the java-class property value
*/
public static String getJavaClass(final Procedure proc) {
String javaClass = null;
ModelObjectExtensionAssistant assistant = (ModelObjectExtensionAssistant)ExtensionPlugin.getInstance().getRegistry().getModelExtensionAssistant(NAMESPACE_PROVIDER.getNamespacePrefix());
if(assistant!=null) {
try {
javaClass = assistant.getPropertyValue(proc, RelationalModelExtensionConstants.PropertyIds.JAVA_CLASS);
} catch (Exception ex) {
String msg = RelationalPlugin.Util.getString("ProcedureFunctionRule.errorGettingJavaClass", proc.getName()); //$NON-NLS-1$
RelationalPlugin.Util.log(IStatus.ERROR,ex,msg);
}
}
return javaClass;
}
/**
* Get the java-method property from the supplied Procedure
* @param proc the supplied Procedure
* @return the java-method property value
*/
public static String getJavaMethod(final Procedure proc) {
String javaMethod = null;
ModelObjectExtensionAssistant assistant = (ModelObjectExtensionAssistant)ExtensionPlugin.getInstance().getRegistry().getModelExtensionAssistant(NAMESPACE_PROVIDER.getNamespacePrefix());
if(assistant!=null) {
try {
javaMethod = assistant.getPropertyValue(proc, RelationalModelExtensionConstants.PropertyIds.JAVA_METHOD);
} catch (Exception ex) {
String msg = RelationalPlugin.Util.getString("ProcedureFunctionRule.errorGettingJavaMethod", proc.getName()); //$NON-NLS-1$
RelationalPlugin.Util.log(IStatus.ERROR,ex,msg);
}
}
return javaMethod;
}
/**
* Get the function-category property from the supplied Procedure
* @param proc the supplied Procedure
* @return the function-category property value
*/
public static String getFunctionCategory(final Procedure proc) {
String javaMethod = null;
ModelObjectExtensionAssistant assistant = (ModelObjectExtensionAssistant)ExtensionPlugin.getInstance().getRegistry().getModelExtensionAssistant(NAMESPACE_PROVIDER.getNamespacePrefix());
if(assistant!=null) {
try {
javaMethod = assistant.getPropertyValue(proc, RelationalModelExtensionConstants.PropertyIds.FUNCTION_CATEGORY);
} catch (Exception ex) {
String msg = RelationalPlugin.Util.getString("ProcedureFunctionRule.errorGettingFunctionCategory", proc.getName()); //$NON-NLS-1$
RelationalPlugin.Util.log(IStatus.ERROR,ex,msg);
}
}
return javaMethod;
}
/**
* Get the Udf jarPath property from the supplied ScalarFunction
* @param proc the supplied Procedure
* @return the Udf jarPath property value
*/
public static String getUdfJarPath(final Procedure proc) {
String udfJarPath = null;
ModelObjectExtensionAssistant assistant = (ModelObjectExtensionAssistant)ExtensionPlugin.getInstance().getRegistry().getModelExtensionAssistant(NAMESPACE_PROVIDER.getNamespacePrefix());
if(assistant!=null) {
try {
udfJarPath = assistant.getPropertyValue(proc, RelationalModelExtensionConstants.PropertyIds.UDF_JAR_PATH);
} catch (Exception ex) {
String msg = RelationalPlugin.Util.getString("ProcedureFunctionRule.errorGettingJarPath", proc.getName()); //$NON-NLS-1$
RelationalPlugin.Util.log(IStatus.ERROR,ex,msg);
}
}
return udfJarPath;
}
/**
* Check that View Procedure function has a return parameter, and it's valid
* @param procedure the Procedure
* @param result the ValidationResult
*/
private final void validateReturnParameter(Procedure procedure, ValidationResult result) {
List<EObject> params = procedure.getParameters();
// First check for at least one parameter
if(params.size()==0) {
ValidationProblem problem = new ValidationProblemImpl(0, IStatus.ERROR ,RelationalPlugin.Util.getString("ProcedureFunctionRule.returnParmeterIsRequired")); //$NON-NLS-1$
result.addProblem(problem);
return;
}
// Next verify that there is a return parameter
ProcedureParameter returnParam = null;
for (EObject param : params) {
if (param instanceof ProcedureParameter) {
DirectionKind direction = ((ProcedureParameter)param).getDirection();
int directionKind = direction.getValue();
if (directionKind == DirectionKind.RETURN) {
returnParam = (ProcedureParameter)param;
break;
}
}
}
// Return Parameter not found
if(returnParam==null) {
ValidationProblem problem = new ValidationProblemImpl(0, IStatus.ERROR ,RelationalPlugin.Util.getString("ProcedureFunctionRule.returnParmeterIsRequired")); //$NON-NLS-1$
result.addProblem(problem);
return;
}
}
/**
* Check that specified string is valid Java identifier. If not, create problems on the validation result.
* @param identifier String to check
* @param strName String to use in exception message
* @param allowMultiple True if multiple identifiers are allowed, as in a class name
*/
private final void validateJavaIdentifier(String identifier, String strName, boolean allowMultiple, ValidationResult result) {
// First check first character
if(!CoreStringUtil.isEmpty(identifier)) {
char firstChar = identifier.charAt(0);
if(! Character.isJavaIdentifierStart(firstChar)) {
ValidationProblem problem = new ValidationProblemImpl(0, IStatus.ERROR , strName+RelationalPlugin.Util.getString("ProcedureFunctionRule.hasInvalidFirstChar")+'\''+firstChar+'\''); //$NON-NLS-1$
result.addProblem(problem);
}
// Then check the rest of the characters
for(int i=1; i<identifier.length(); i++) {
char ch = identifier.charAt(i);
if(! Character.isJavaIdentifierPart(ch)) {
if(! allowMultiple || ! (ch == '.')) {
ValidationProblem problem = new ValidationProblemImpl(0, IStatus.ERROR , strName+RelationalPlugin.Util.getString("ProcedureFunctionRule.hasInvalidChar")+'\''+ch+'\''); //$NON-NLS-1$
result.addProblem(problem);
}
}
}
if(identifier.charAt(identifier.length()-1) == '.') {
ValidationProblem problem = new ValidationProblemImpl(0, IStatus.ERROR ,strName+RelationalPlugin.Util.getString("ProcedureFunctionRule.cannotEndWithDot")); //$NON-NLS-1$
result.addProblem(problem);
}
}
}
/**
* Get the project's folder that contains the Udf jars. If it doesnt exist, returns null.
* @param project the supplied project
* @return the Udf jar folder within the project, null if non-existent.
*/
public static IFolder getUdfJarFolder(IProject project) {
IFolder libFolder = null;
if(project!=null) {
IResource[] resources = null;
try {
resources = project.members();
} catch (CoreException ex) {
return null;
}
// Iterate the child resources, looking for lib folder
if(resources!=null) {
for(int i=0; i<resources.length; i++) {
IResource theResc = resources[i];
if(theResc instanceof IFolder && VdbFolders.UDF.getReadFolder().equalsIgnoreCase(((IFolder)theResc).getName())) {
libFolder = (IFolder)theResc;
break;
}
}
}
}
return libFolder;
}
/**
* Determine if a jar with the specified name is in the specified folder
* @param folder the supplied folder
* @param jarFileName the name of the jar to find
* @return 'true' if found, 'false' if not.
*/
public static boolean isJarInFolder(IFolder folder,String jarFileName) {
boolean found = false;
// Iterate the child resources, looking for lib folder
if(folder!=null) {
try {
IResource[] folderEntries = folder.members();
for(int j=0; j<folderEntries.length; j++) {
IResource folderEntry = folderEntries[j];
if( folderEntry instanceof IFile && ((IFile)folderEntry).getProjectRelativePath().toString().equalsIgnoreCase(jarFileName) ) {
found = true;
break;
}
}
} catch (CoreException ex) {
ModelerCore.Util.log(IStatus.ERROR,ex,RelationalPlugin.Util.getString("ProcedureFunctionRule.errorWithJarLookupInFolder", folder.getName())); //$NON-NLS-1$
}
}
return found;
}
}