jaxx.runtime.validator.field.CollectionUniqueKeyValidator Maven / Gradle / Ivy
/*
* *##%
* JAXX Runtime
* Copyright (C) 2008 - 2009 CodeLutin
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Lesser Public License for more details.
*
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* .
* ##%*
*/
package jaxx.runtime.validator.field;
import com.opensymphony.xwork2.validator.ValidationException;
import com.opensymphony.xwork2.validator.validators.FieldExpressionValidator;
import org.apache.commons.lang.builder.HashCodeBuilder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* Un validateur basé sur {@link FieldExpressionValidator} qui valide une clef
* unique sur une collection.
*
* Le {@link #fieldName} sert à récupérer la propriété de type de collection du
* bean.
*
* @author chemit
*/
public class CollectionUniqueKeyValidator extends FieldExpressionValidator {
/**
* pour indiquer la propriété qui contient la liste à valider.
*
* Si cette prorpiété n'est pas renseignée alors on utilise la
* {@link #getFieldName()} pour obtenir la collection.
*
* Cela permet d'effectuer une validation si une collection mais portant
* en fait sur un autre champs
*
* @since 1.5
*/
protected String collectionFieldName;
/**
* la liste des propriétés d'une entrée de la collection qui définit la
* clef unique.
*/
protected String[] keys;
/**
* Une propriété optionnelle pour valider que l'objet reflétée par cette
* propriété ne viole pas l'intégrité de la clef unique.
* Cela permet de valider l'unicité sans que l'objet soit dans la collection
*/
protected String againstProperty;
/**
* Lors de l'utilisation de la againstProperty et qu'un ne peut pas utiliser
* le equals sur l'objet, on peut spécifier une expression pour exclure des
* tests lors de la recherche de la violation de clef unique.
*/
protected String againstIndexExpression;
public String getCollectionFieldName() {
return collectionFieldName;
}
public void setCollectionFieldName(String collectionFieldName) {
this.collectionFieldName = collectionFieldName;
}
public String[] getKeys() {
return keys;
}
public void setKeys(String[] keys) {
if (keys != null && keys.length == 1 && keys[0].indexOf(',') != -1) {
this.keys = keys[0].split(",");
} else {
this.keys = keys;
}
}
public String getAgainstProperty() {
return againstProperty;
}
public void setAgainstProperty(String againstProperty) {
this.againstProperty = againstProperty;
}
public String getAgainstIndexExpression() {
return againstIndexExpression;
}
public void setAgainstIndexExpression(String againstIndexExpression) {
this.againstIndexExpression = againstIndexExpression;
}
@Override
public void validate(Object object) throws ValidationException {
if (keys == null || keys.length == 0) {
throw new ValidationException("no unique keys defined");
}
String fieldName = getFieldName();
Collection> col = getCollection(object);
if (log.isDebugEnabled()) {
log.debug("collection found : " + col);
}
Object againstBean = againstProperty == null ? null :
getFieldValue(againstProperty, object);
if (log.isDebugEnabled()) {
log.debug("againtBean = " + againstBean);
}
Integer againstIndex = (Integer) (againstIndexExpression == null ?
-1 : getFieldValue(againstIndexExpression, object));
if (againstIndex == null) {
againstIndex = -1;
}
if (againstBean == null && col.size() < 2) {
// la liste ne contient pas deux entrées donc c'est valide
return;
}
boolean answer = true;
Integer againstHashCode = againstBean == null ?
null : getUniqueKeyHashCode(againstBean);
if (log.isDebugEnabled()) {
log.debug("hash for new key " + againstHashCode);
}
List hashCodes = new ArrayList();
int index = 0;
for (Object o : col) {
Integer hash = getUniqueKeyHashCode(o);
if (log.isDebugEnabled()) {
log.debug("hash for object " + o + " = " + hash);
}
if (againstBean == null) {
if (hashCodes.contains(hash)) {
// on a deja rencontre cette clef unique,
// donc la validation a echouee
answer = false;
if (log.isDebugEnabled()) {
log.debug("Found same hashcode, not unique!");
}
break;
}
} else {
// utilisation de againstBean
if (againstIndex != -1) {
if (index != againstIndex &&
hash.equals(againstHashCode)) {
// on a deja rencontre cette clef unique,
// donc la validation a echouee
answer = false;
break;
}
} else {
if (!againstBean.equals(o) &&
hash.equals(againstHashCode)) {
// on a deja rencontre cette clef unique,
// donc la validation a echouee
answer = false;
break;
}
}
}
// nouveau hashcode enregistre
hashCodes.add(hash);
// index suivant
index++;
}
if (!answer) {
addFieldError(fieldName, object);
}
}
/**
* Calcule pour une entrée donné, le hash de la clef unique
*
* @param o l'entree de la collection dont on va calculer le hash de
* la clef unique
* @return le hashCode calclé de la clef unique sur l'entrée donné
* @throws ValidationException if any pb to retreave properties values
*/
protected Integer getUniqueKeyHashCode(Object o)
throws ValidationException {
// calcul du hash à la volée
HashCodeBuilder builder = new HashCodeBuilder();
for (String key : this.keys) {
Object property = getFieldValue(key, o);
builder.append(property);
}
return builder.toHashCode();
}
/**
* @param object the incoming object containing the collection to test
* @return the collection of the incoming object given by the fieldName
* property
* @throws ValidationException if any pb to retreave the collection
*/
protected Collection> getCollection(Object object)
throws ValidationException {
String fieldName = getCollectionFieldName();
if (fieldName == null || fieldName.trim().isEmpty()) {
// on travaille directement sur le fieldName
fieldName = getFieldName();
}
Object obj = null;
// obtain the collection to test
try {
obj = getFieldValue(fieldName, object);
} catch (ValidationException e) {
throw e;
} catch (Exception e) {
// let this pass, but it will be logged right below
}
if (obj == null) {
// la collection est nulle, donc on renvoie une collection vide
return java.util.Collections.EMPTY_LIST;
}
if (!Collection.class.isInstance(obj)) {
throw new ValidationException("field " + fieldName +
" is not a collection type! (" + obj.getClass() + ')');
}
return (Collection>) obj;
}
@Override
public String getValidatorType() {
return "collectionUniqueKey";
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy