Created
June 29, 2010 15:17
-
-
Save robfletcher/457351 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.github.robfletcher.grails.validation | |
import org.codehaus.groovy.grails.validation.AbstractConstraint | |
import org.springframework.validation.Errors | |
class AcyclicConstraint extends AbstractConstraint { | |
static final String DEFAULT_MESSAGE_CODE = "default.acyclic.violation.message" | |
static final String NAME = "acyclic" | |
private boolean validateConstraint | |
protected void processValidate(Object target, Object propertyValue, Errors errors) { | |
if (validateConstraint && propertyValue) { | |
if (isCyclic(target, propertyValue)) { | |
def args = [constraintPropertyName, constraintOwningClass, propertyValue] as Object[] | |
rejectValue(target, errors, DEFAULT_MESSAGE_CODE, "${NAME}.violation", args) | |
} | |
} | |
} | |
void setParameter(Object constraintParameter) { | |
if (!(constraintParameter instanceof Boolean)) { | |
throw new IllegalArgumentException("Parameter for constraint [$NAME] of property [$constraintPropertyName] of class [$constraintOwningClass] must be a boolean value") | |
} | |
validateConstraint = constraintParameter | |
super.setParameter(constraintParameter) | |
} | |
boolean supports(Class type) { true } | |
String getName() { NAME } | |
private boolean isCyclic(original, node) { | |
boolean cyclic = false | |
while (node != null) { | |
if (node.id == original.id) { | |
cyclic = true | |
break | |
} | |
node = node."$propertyName" | |
} | |
return cyclic | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.github.robfletcher.grails.validation | |
import grails.test.GrailsUnitTestCase | |
import org.apache.commons.lang.math.RandomUtils | |
import org.springframework.validation.BindException | |
import org.springframework.validation.Errors | |
import static org.hamcrest.MatcherAssert.assertThat | |
import static org.hamcrest.Matchers.hasItem | |
class AcyclicConstraintTests extends GrailsUnitTestCase { | |
AcyclicConstraint constraint | |
TestBean target | |
Errors errors | |
void setUp() { | |
super.setUp() | |
constraint = new AcyclicConstraint() | |
constraint.owningClass = TestBean | |
constraint.propertyName = "parent" | |
constraint.parameter = true | |
target = new TestBean(id: RandomUtils.nextLong()) | |
errors = new BindException(target, "target") | |
} | |
void testNullValueIsValid() { | |
constraint.validate target, target.parent, errors | |
assertFalse "Should have no errors", errors.hasErrors() | |
} | |
void testSelfRelationIsInvalid() { | |
target.parent = target | |
constraint.validate target, target.parent, errors | |
assertTrue "Should have errors", errors.hasErrors() | |
assertThat "Error codes on 'parent' property", errors.getFieldErrors("parent").code, hasItem("acyclic.violation") | |
} | |
void testCircularRelationshipIsInvalid() { | |
target.parent = new TestBean(id: RandomUtils.nextLong(), parent: target) | |
constraint.validate target, target.parent, errors | |
assertTrue "Should have errors", errors.hasErrors() | |
assertThat "Error codes on 'parent' property", errors.getFieldErrors("parent").code, hasItem("acyclic.violation") | |
} | |
void testAcyclicRelationshipIsValid() { | |
target.parent = new TestBean(id: RandomUtils.nextLong()) | |
constraint.validate target, target.parent, errors | |
assertFalse "Should not have errors", errors.hasErrors() | |
} | |
void testCircularRelationshipIsValidIfConstraintIsDisabled() { | |
target.parent = new TestBean(id: RandomUtils.nextLong(), parent: target) | |
constraint.parameter = false | |
constraint.validate target, target.parent, errors | |
assertFalse "Should not have errors", errors.hasErrors() | |
} | |
} | |
class TestBean { | |
long id | |
TestBean parent | |
boolean equals(Object obj) { | |
if (this.is(obj)) return true | |
if (obj == null) return false | |
if (!(obj instanceof TestBean)) return false | |
return id == obj.id | |
} | |
int hashCode() { | |
return id.hashCode() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment