Skip to content

Commit

Permalink
TEIIDDES-1211 Added unique sibling name validation rule that crosses …
Browse files Browse the repository at this point in the history
…object types
  • Loading branch information
blafond committed May 22, 2013
1 parent 978f920 commit 15f66b5
Show file tree
Hide file tree
Showing 6 changed files with 232 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ public interface ValidationPreferences {
* Preference name for siblings having same nameInSource rule.
*/
String RELATIONAL_SIBLING_NAME_IN_SOURCE = "relationalPreferences.siblingNameInSource"; //$NON-NLS-1$

/**
* Preference name for siblings having same nameInSource rule.
*/
String RELATIONAL_SIBLING_NAME = "relationalPreferences.siblingName"; //$NON-NLS-1$

/**
* Preference name for string/char datatype columns whose length is undefined.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pluginName = Relational Metamodel Plug-in
pluginProvider = JBoss by Red Hat
metamodelName = Relational
siblingNameInSource = Name in source conflicts with sibling entities:
siblingName = Name conflicts with sibling entities:
missingNameInSource = Table missing name in source value:
missingColumnLength = Missing length property on columns with string or character types:
missingColumnPrecision = Missing precision property on columns with numeric types:
Expand Down
7 changes: 7 additions & 0 deletions plugins/org.teiid.designer.metamodels.relational/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,13 @@
default="ignore"
category="Relational">
</preference>
<preference
toolTip="Sibling names the same ?"
name="siblingName"
label="%siblingName"
default="error"
category="Relational">
</preference>
<preference
toolTip="Indexes with columns from more than one table. Many relational database management systems don't allow such indexes."
name="crossTableIndexes"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.teiid.designer.metamodels.relational.aspects.validation.rules.MissingNameInSourceRule;
import org.teiid.designer.metamodels.relational.aspects.validation.rules.ProcedureParametersRule;
import org.teiid.designer.metamodels.relational.aspects.validation.rules.ProcedureFunctionRule;
import org.teiid.designer.metamodels.relational.aspects.validation.rules.RelationalChildrenNameValidationRule;
import org.teiid.designer.metamodels.relational.aspects.validation.rules.RelationalStringNameRule;
import org.teiid.designer.metamodels.relational.aspects.validation.rules.SiblingNameInSourceRule;
import org.teiid.designer.metamodels.relational.aspects.validation.rules.SourceProcedureUniquenessRule;
Expand All @@ -46,6 +47,7 @@ public abstract class RelationalEntityAspect extends AbstractValidationAspect {
public static final ValidationRule MISSING_COLUMN_LENGTH_RULE = new MissingColumnLengthRule();
public static final ValidationRule MISSING_COLUMN_PRECISION_RULE = new MissingColumnPrecisionRule();
public static final ValidationRule SIBLING_NAME_IN_SOURCE_RULE = new SiblingNameInSourceRule();
public static final ValidationRule SIBLING_NAME_RULE = new RelationalChildrenNameValidationRule();
public static final ValidationRule COLUMN_INTEGER_DATATYPE_RULE = new ColumnIntegerDatatypeRule();
public static final ValidationRule FOREIGN_KEY_COLUMNS_RULE = new ForeignKeyColumnsRule();
public static final ValidationRule UNIQUE_KEY_COLUMNS_RULE = new UniqueKeyColumnsRule();
Expand All @@ -71,6 +73,7 @@ protected RelationalEntityAspect(final MetamodelEntity entity) {
public ValidationRuleSet getValidationRules() {
addRule(NAME_RULE);
addRule(LENGTH_RULE);
addRule(SIBLING_NAME_RULE);
addRule(SIBLING_NAME_IN_SOURCE_RULE);
return super.getValidationRules();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
/*
* 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 java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.emf.ecore.EObject;
import org.teiid.core.designer.util.CoreStringUtil;
import org.teiid.designer.core.ModelerCore;
import org.teiid.designer.core.ValidationPreferences;
import org.teiid.designer.core.util.ModelVisitorProcessor;
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.metamodels.core.ModelAnnotation;
import org.teiid.designer.metamodels.core.ModelType;
import org.teiid.designer.metamodels.relational.Index;
import org.teiid.designer.metamodels.relational.Procedure;
import org.teiid.designer.metamodels.relational.RelationalEntity;
import org.teiid.designer.metamodels.relational.RelationalPlugin;
import org.teiid.designer.metamodels.relational.Table;
import org.teiid.designer.metamodels.relational.util.RelationalUtil;

/**
* This rule is required to check sibling names of different metamodel types for uniqueness.
*
* Example is an INDEX and a TABLE with same name. This may cause issues when exporting both to DDL and attempting to
* execute that DDL in a DB.
*/
public class RelationalChildrenNameValidationRule implements ObjectValidationRule {

private static final String nameFeatureName = RelationalPlugin.Util.getString("_UI_RelationalEntity_name_feature"); //$NON-NLS-1$
private static final String RULE_NAME = RelationalChildrenNameValidationRule.class.getName();

/*
* @See org.teiid.designer.core.validation.ObjectValidationRule#validate(org.eclipse.emf.ecore.EObject, org.teiid.designer.core.validation.ValidationContext)
*/
@Override
public void validate(final EObject eObject, final ValidationContext context) {

// nothing to validate if there are no siblings
if(!shouldRun(eObject, context)) {
return;
}

// get siblings to validate
List siblings = getSiblingsForUniqueNameCheck(eObject);
if(siblings.isEmpty()) {
return;
}

final int statusForCaseSensitive = getPreferenceStatus(context);
final int statusForCaseInsensitive = statusForCaseSensitive == IStatus.ERROR ? IStatus.WARNING : statusForCaseSensitive;

ValidationResults validationResults = new ValidationResults(siblings.size());
for(int i=0; i < siblings.size(); i++) {
EObject siblingI = (EObject)siblings.get(i);
String nameA = getName(siblingI);
final ValidationResult result = validationResults.get(i,siblingI);

if(!CoreStringUtil.isEmpty(nameA)) {
for(int j=i+1; j < siblings.size(); j++) {
EObject siblingJ = (EObject)siblings.get(j);
String nameB = getName(siblings.get(j));
if(!CoreStringUtil.isEmpty(nameB)) {
if(nameA.equals(nameB)) {
// create validation problem and add it to the result
final Object[] params = new Object[]{nameA,nameB,nameFeatureName};
final String msg = RelationalPlugin.Util.getString("RelationalChildrenNameValidationRule.failure_message_case_sensitive",params); //$NON-NLS-1$
ValidationProblem problem = new ValidationProblemImpl(0, statusForCaseSensitive,msg);
problem.setHasPreference(context.hasPreferences());
result.addProblem(problem);
// creating additional validation results on other sibling
// note this would create markers with duplicate messages but targets
// will be different
final ValidationResult resultJ = validationResults.get(j,siblingJ);
ValidationProblem problemJ = new ValidationProblemImpl(0, statusForCaseSensitive,msg);
problemJ.setHasPreference(context.hasPreferences());
resultJ.addProblem(problemJ);
} else if(nameA.equalsIgnoreCase(nameB)) {
// create validation problem and add it to the result
final Object[] params = new Object[]{nameA,nameB,nameFeatureName};
final String msg = RelationalPlugin.Util.getString("RelationalChildrenNameValidationRule.failure_message_case_insensitive",params); //$NON-NLS-1$
ValidationProblem problem = new ValidationProblemImpl(0, statusForCaseInsensitive ,msg);
problem.setHasPreference(context.hasPreferences());
result.addProblem(problem);
// creating additional validation results on other sibling
// note this would create markers with duplicate messages but targets
// will be different
final ValidationResult resultJ = validationResults.get(j,siblingJ);
ValidationProblem problemJ = new ValidationProblemImpl(0, statusForCaseInsensitive,msg);
problemJ.setHasPreference(context.hasPreferences());
resultJ.addProblem(problemJ);
}
}
}
}
// add the result to the context
context.addResult(result);
}
}

//############################################################################################################################
//# P R O T E C T E D M E T H O D S #
//############################################################################################################################

protected class ValidationResults {
private final ValidationResult[] results;
public ValidationResults(final int size) {
this.results = new ValidationResult[size];
}
public ValidationResult get(final int index, final EObject object ) {
ValidationResult result = this.results[index];
if ( result == null ) {
result = new ValidationResultImpl(object);
this.results[index] = result;
}
return result;
}
}

protected int getPreferenceStatus(final ValidationContext context) {
return context.getPreferenceStatus(ValidationPreferences.RELATIONAL_SIBLING_NAME, IStatus.OK);
}

/**
* This method groups siblings into the following domains, and chooses only those siblings that are in
* the same domain as the supplied object.
* <ul>
* <li>{@link Table} and {@link View} instances</li>
* <li>{@link Procedure} instances</li>
* <li>{@link Index} instances</li>
* <li></li>
* <li></li>
* </ul>
* @see org.teiid.designer.core.validation.rules.StringNameRule#getSiblingsForUniqueNameCheck(org.eclipse.emf.ecore.EObject)
*/
protected List getSiblingsForUniqueNameCheck(final EObject eObject) {
Object parent = eObject.eContainer();
if ( parent == null ) {
parent = eObject.eResource();
}

if ( eObject instanceof Table ||
eObject instanceof Procedure ||
eObject instanceof Index ) {
List siblings = new ArrayList();
siblings.addAll(RelationalUtil.findTables(parent, ModelVisitorProcessor.DEPTH_ONE));
siblings.addAll(RelationalUtil.findProcedures(parent, ModelVisitorProcessor.DEPTH_ONE));
siblings.addAll(RelationalUtil.findIndexes(parent, ModelVisitorProcessor.DEPTH_ONE));
return siblings;
}
return Collections.EMPTY_LIST;
}

//############################################################################################################################
//# P R I V A T E M E T H O D S #
//############################################################################################################################

// private String getNameInSource(final Object sibling) {
// if(sibling instanceof RelationalEntity) {
// RelationalEntity entity = (RelationalEntity) sibling;
// return entity.getNameInSource();
// }
// return null;
// }

private String getName(final Object sibling) {
if(sibling instanceof RelationalEntity) {
RelationalEntity entity = (RelationalEntity) sibling;
return entity.getName();
}
return ModelerCore.getModelEditor().getModelRelativePathIncludingModel((EObject) sibling).toString();
}

private boolean shouldRun(final EObject eObject, final ValidationContext context) {
if(getPreferenceStatus(context) == IStatus.OK) {
return false;
}

// run the rule only for physical models
try {
ModelAnnotation modelAnnotation = ModelerCore.getModelEditor().getModelAnnotation(eObject);
int modelType = modelAnnotation.getModelType().getValue();
if(modelType != ModelType.PHYSICAL) {
return false;
}
} catch(Exception e) {
RelationalPlugin.Util.log(IStatus.ERROR, e, e.getMessage());
}

// type of object this rule is being run on.
String objType = eObject.eClass().getName();
// this rule is being run once per object type per parent
if(context.hasRunRule(eObject, RULE_NAME+objType)) {
return false;
}
context.recordRuleRun(eObject, RULE_NAME+objType);

return true;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,8 @@ SiblingNameInSourceRule.failure_message_case_sensitive=Siblings {0} and {1} have
SiblingNameInSourceRule.failure_message_case_insensitive=The {2} of siblings {0} and {1} varies only by case
SiblingNameInSourceRule.Expected_a_relational_entity_object_3=Expected a relational entity object
SiblingNameInSourceRule.Expected_a_relational_entity_object_4=Expected a relational entity object
RelationalChildrenNameValidationRule.failure_message_case_sensitive=Siblings {0} and {1} have the same {2}
RelationalChildrenNameValidationRule.failure_message_case_insensitive=The {2} of siblings {0} and {1} varies only by case
MissingNameInSourceRule.warning_message=Relational table {0} does not have a {1}
RelationalStringNameValidator.or_other_valid_table_characters= '.' and '_'
MissingColumnLengthRule.failure_message=A column with a string/character datatype was found with a missing or invalid length. The column will allocated a default runtime length of 4000 chars
Expand Down

0 comments on commit 15f66b5

Please sign in to comment.