r292 - in trunk: coser-business/src/main/java/fr/ifremer/coser/control coser-business/src/main/java/fr/ifremer/coser/services coser-business/src/test/java/fr/ifremer/coser/services coser-ui/src/main/java/fr/ifremer/coser coser-ui/src/main/java/fr/ifremer/coser/ui/control
Author: chatellier Date: 2010-11-26 14:03:54 +0000 (Fri, 26 Nov 2010) New Revision: 292 Log: Refactoring validation > control Added: trunk/coser-business/src/main/java/fr/ifremer/coser/control/ControlError.java trunk/coser-business/src/main/java/fr/ifremer/coser/control/ControlErrorGroup.java trunk/coser-business/src/main/java/fr/ifremer/coser/control/DiffCatchLengthControlError.java trunk/coser-business/src/main/java/fr/ifremer/coser/services/ControlService.java trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/control/ControlErrorTreeRenderer.java trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/control/GlobalControlErrorModel.java Removed: trunk/coser-business/src/main/java/fr/ifremer/coser/control/DiffCatchLengthValidationError.java trunk/coser-business/src/main/java/fr/ifremer/coser/control/GlobalValidationGroup.java trunk/coser-business/src/main/java/fr/ifremer/coser/control/ValidationError.java trunk/coser-business/src/main/java/fr/ifremer/coser/services/ValidationService.java trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/control/ControlValidationRenderer.java trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/control/GlobalValidationModel.java Modified: trunk/coser-business/src/main/java/fr/ifremer/coser/services/PublicationService.java trunk/coser-business/src/test/java/fr/ifremer/coser/services/PublicationServiceTest.java trunk/coser-business/src/test/java/fr/ifremer/coser/services/ValidationServiceTest.java trunk/coser-ui/src/main/java/fr/ifremer/coser/Coser.java trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/control/ControlHandler.java trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/control/ControlView.jaxx Copied: trunk/coser-business/src/main/java/fr/ifremer/coser/control/ControlError.java (from rev 260, trunk/coser-business/src/main/java/fr/ifremer/coser/control/ValidationError.java) =================================================================== --- trunk/coser-business/src/main/java/fr/ifremer/coser/control/ControlError.java (rev 0) +++ trunk/coser-business/src/main/java/fr/ifremer/coser/control/ControlError.java 2010-11-26 14:03:54 UTC (rev 292) @@ -0,0 +1,214 @@ +/* + * #%L + * + * + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2010 Ifremer, Codelutin, Chatellier Eric + * %% + * 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 + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package fr.ifremer.coser.control; + +import java.io.Serializable; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import fr.ifremer.coser.CoserConstants.Category; +import fr.ifremer.coser.CoserConstants.ValidationLevel; + +/** + * Class regroupant les erreurs de controle d'une entité, son type (erreur, + * warning...) et les information supplémentaires permettant de la corriger + * (numéro de ligne). + * + * @author chatellier + * @version $Revision$ + * + * Last update : $Date$ + * By : $Author$ + */ +public class ControlError implements Serializable { + + /** serialVersionUID. */ + private static final long serialVersionUID = -1806823191454701123L; + + /** Category de l'erreur. */ + protected Category category; + + /** Niveau de l'erreur (fatal, error, warning, info). */ + protected ValidationLevel level; + + /** Message explicitant l'erreur de validation. */ + protected String message; + + /** Message donnant plus de précision sur l'erreur. */ + protected String detailMessage; + + /** Tip message (can be {@code null}). */ + protected String tipMessage; + + /** + * Numero des lignes dans le fichier CSV. + * + * Peut être vide si l'erreur n'est pas associé à une ligne specifiques. + */ + protected Set<String> lineNumbers; + + public ControlError() { + super(); + + lineNumbers = new HashSet<String>(); + } + + /** + * Category de l'erreur. + * + * Peut etre null si l'erreur n'est pas associées à une category en particulier. + * + * @return category de l'erreur + */ + public Category getCategory() { + return category; + } + + /** + * Category de l'erreur. + * + * @param category category de l'erreur + */ + public void setCategory(Category category) { + this.category = category; + } + + /** + * Niveau de l'erreur (fatal, error, warning, info). + * + * @return level + */ + public ValidationLevel getLevel() { + return level; + } + + /** + * Niveau de l'erreur (fatal, error, warning, info). + * + * @param level level + */ + public void setLevel(ValidationLevel level) { + this.level = level; + } + + /** + * Message explicitant l'erreur de validation. + * + * @return message + */ + public String getMessage() { + return message; + } + + /** + * Message explicitant l'erreur de validation. + * + * @param message message + */ + public void setMessage(String message) { + this.message = message; + } + + /** + * Message donnant plus de précision sur l'erreur. + * + * Si le message de detail est null, le message est retourné. + * + * @return detail message + */ + public String getDetailMessage() { + String result = detailMessage; + if (result == null) { + result = message; + } + return result; + } + + /** + * Message donnant plus de précision sur l'erreur. + * + * @param detailMessage detail message + */ + public void setDetailMessage(String detailMessage) { + this.detailMessage = detailMessage; + } + + /** + * Get tip message (can be {@code null}). + * + * @return tip message + */ + public String getTipMessage() { + return tipMessage; + } + + /** + * Set tip message (can be {@code null}). + * + * @param tipMessage tip message + */ + public void setTipMessage(String tipMessage) { + this.tipMessage = tipMessage; + } + + /** + * Numero des lignes dans le fichier CSV. + * + * Peut être vide si l'erreur n'est pas associé a une ligne specifiques. + * + * @return line number + */ + public Set<String> getLineNumbers() { + return lineNumbers; + } + + /** + * Numero des lignes dans le fichier CSV. + * + * Peut être vide si l'erreur n'est pas associé a une ligne specifiques. + * + * @param lineNumber line number + */ + public void addLineNumber(String lineNumber) { + lineNumbers.add(lineNumber); + } + + /** + * Numero des lignes dans le fichier CSV. + * + * @param lineNumbers line numbers to add + */ + public void addAllLineNumber(Collection<String> lineNumbers) { + this.lineNumbers.addAll(lineNumbers); + } + + @Override + public String toString() { + return "ValidationError [level=" + level + ", message=" + message + + ", lineNumber=" + lineNumbers + "]"; + } +} Copied: trunk/coser-business/src/main/java/fr/ifremer/coser/control/ControlErrorGroup.java (from rev 284, trunk/coser-business/src/main/java/fr/ifremer/coser/control/GlobalValidationGroup.java) =================================================================== --- trunk/coser-business/src/main/java/fr/ifremer/coser/control/ControlErrorGroup.java (rev 0) +++ trunk/coser-business/src/main/java/fr/ifremer/coser/control/ControlErrorGroup.java 2010-11-26 14:03:54 UTC (rev 292) @@ -0,0 +1,119 @@ +/* + * #%L + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2010 Ifremer, Codelutin, Chatellier Eric + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +package fr.ifremer.coser.control; + +import fr.ifremer.coser.CoserConstants.Category; +import fr.ifremer.coser.CoserConstants.ValidationLevel; + +/** + * Global validation group node. + * + * @author chatellier + * @version $Revision$ + * + * Last update : $Date$ + * By : $Author$ + */ +public class ControlErrorGroup implements Comparable<ControlErrorGroup> { + + protected final Category category; + + protected final ValidationLevel validationLevel; + + protected final String message; + + /** + * @param category + * @param validationLevel + * @param message + */ + public ControlErrorGroup(Category category, ValidationLevel validationLevel, String message) { + super(); + this.category = category; + this.validationLevel = validationLevel; + this.message = message; + } + + public Category getCategory() { + return category; + } + + public ValidationLevel getValidationLevel() { + return validationLevel; + } + + public String getMessage() { + return message; + } + + /* + * @see java.lang.Comparable#compareTo(java.lang.Object) + */ + @Override + public int compareTo(ControlErrorGroup o) { + int compare = o.getValidationLevel().compareTo(validationLevel); + if (compare == 0) { + compare = o.getMessage().compareTo(message); + } + return compare; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((category == null) ? 0 : category.hashCode()); + result = prime * result + ((message == null) ? 0 : message.hashCode()); + result = prime * result + ((validationLevel == null) ? 0 : validationLevel.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + ControlErrorGroup other = (ControlErrorGroup) obj; + if (message == null) { + if (other.message != null) { + return false; + } + } else if (!message.equals(other.message)) { + return false; + } + if (validationLevel != other.validationLevel) { + return false; + } + if (category != other.category) { + return false; + } + return true; + } +} Copied: trunk/coser-business/src/main/java/fr/ifremer/coser/control/DiffCatchLengthControlError.java (from rev 284, trunk/coser-business/src/main/java/fr/ifremer/coser/control/DiffCatchLengthValidationError.java) =================================================================== --- trunk/coser-business/src/main/java/fr/ifremer/coser/control/DiffCatchLengthControlError.java (rev 0) +++ trunk/coser-business/src/main/java/fr/ifremer/coser/control/DiffCatchLengthControlError.java 2010-11-26 14:03:54 UTC (rev 292) @@ -0,0 +1,53 @@ +/* + * #%L + * + * + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2010 Ifremer, Codelutin, Chatellier Eric + * %% + * 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 + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package fr.ifremer.coser.control; + +/** + * Erreur de controle specifique pour les différences des nombres entre + * les captures et les tailles pour utilisation typée de cette erreur + * (export html) et information supplémentaires (especes). + * + * @author chatellier + * @version $Revision$ + * + * Last update : $Date$ + * By : $Author$ + */ +public class DiffCatchLengthControlError extends ControlError { + + /** serialVersionUID. */ + private static final long serialVersionUID = -3254763296138201677L; + + protected String species; + + public String getSpecies() { + return species; + } + + public void setSpecies(String species) { + this.species = species; + } +} Deleted: trunk/coser-business/src/main/java/fr/ifremer/coser/control/DiffCatchLengthValidationError.java =================================================================== --- trunk/coser-business/src/main/java/fr/ifremer/coser/control/DiffCatchLengthValidationError.java 2010-11-26 13:47:45 UTC (rev 291) +++ trunk/coser-business/src/main/java/fr/ifremer/coser/control/DiffCatchLengthValidationError.java 2010-11-26 14:03:54 UTC (rev 292) @@ -1,53 +0,0 @@ -/* - * #%L - * - * - * $Id$ - * $HeadURL$ - * %% - * Copyright (C) 2010 Ifremer, Codelutin, Chatellier Eric - * %% - * 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 - * <http://www.gnu.org/licenses/lgpl-3.0.html>. - * #L% - */ - -package fr.ifremer.coser.control; - -/** - * Erreur de controle specifique pour les différences des nombres entre - * les captures et les tailles pour utilisation typée de cette erreur - * (export html) et information supplémentaires (especes). - * - * @author chatellier - * @version $Revision$ - * - * Last update : $Date$ - * By : $Author$ - */ -public class DiffCatchLengthValidationError extends ValidationError { - - /** serialVersionUID. */ - private static final long serialVersionUID = -3254763296138201677L; - - protected String species; - - public String getSpecies() { - return species; - } - - public void setSpecies(String species) { - this.species = species; - } -} Deleted: trunk/coser-business/src/main/java/fr/ifremer/coser/control/GlobalValidationGroup.java =================================================================== --- trunk/coser-business/src/main/java/fr/ifremer/coser/control/GlobalValidationGroup.java 2010-11-26 13:47:45 UTC (rev 291) +++ trunk/coser-business/src/main/java/fr/ifremer/coser/control/GlobalValidationGroup.java 2010-11-26 14:03:54 UTC (rev 292) @@ -1,119 +0,0 @@ -/* - * #%L - * $Id$ - * $HeadURL$ - * %% - * Copyright (C) 2010 Ifremer, Codelutin, Chatellier Eric - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU 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 Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * <http://www.gnu.org/licenses/gpl-3.0.html>. - * #L% - */ - -package fr.ifremer.coser.control; - -import fr.ifremer.coser.CoserConstants.Category; -import fr.ifremer.coser.CoserConstants.ValidationLevel; - -/** - * Global validation group node. - * - * @author chatellier - * @version $Revision$ - * - * Last update : $Date$ - * By : $Author$ - */ -public class GlobalValidationGroup implements Comparable<GlobalValidationGroup> { - - protected final Category category; - - protected final ValidationLevel validationLevel; - - protected final String message; - - /** - * @param category - * @param validationLevel - * @param message - */ - public GlobalValidationGroup(Category category, ValidationLevel validationLevel, String message) { - super(); - this.category = category; - this.validationLevel = validationLevel; - this.message = message; - } - - public Category getCategory() { - return category; - } - - public ValidationLevel getValidationLevel() { - return validationLevel; - } - - public String getMessage() { - return message; - } - - /* - * @see java.lang.Comparable#compareTo(java.lang.Object) - */ - @Override - public int compareTo(GlobalValidationGroup o) { - int compare = o.getValidationLevel().compareTo(validationLevel); - if (compare == 0) { - compare = o.getMessage().compareTo(message); - } - return compare; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((category == null) ? 0 : category.hashCode()); - result = prime * result + ((message == null) ? 0 : message.hashCode()); - result = prime * result + ((validationLevel == null) ? 0 : validationLevel.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - GlobalValidationGroup other = (GlobalValidationGroup) obj; - if (message == null) { - if (other.message != null) { - return false; - } - } else if (!message.equals(other.message)) { - return false; - } - if (validationLevel != other.validationLevel) { - return false; - } - if (category != other.category) { - return false; - } - return true; - } -} Deleted: trunk/coser-business/src/main/java/fr/ifremer/coser/control/ValidationError.java =================================================================== --- trunk/coser-business/src/main/java/fr/ifremer/coser/control/ValidationError.java 2010-11-26 13:47:45 UTC (rev 291) +++ trunk/coser-business/src/main/java/fr/ifremer/coser/control/ValidationError.java 2010-11-26 14:03:54 UTC (rev 292) @@ -1,214 +0,0 @@ -/* - * #%L - * - * - * $Id$ - * $HeadURL$ - * %% - * Copyright (C) 2010 Ifremer, Codelutin, Chatellier Eric - * %% - * 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 - * <http://www.gnu.org/licenses/lgpl-3.0.html>. - * #L% - */ - -package fr.ifremer.coser.control; - -import java.io.Serializable; -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; - -import fr.ifremer.coser.CoserConstants.Category; -import fr.ifremer.coser.CoserConstants.ValidationLevel; - -/** - * Class regroupant les erreurs de validation d'une entité, son type (erreur, - * warning...) et les information supplémentaires permettant de la corriger - * (numéro de ligne). - * - * @author chatellier - * @version $Revision$ - * - * Last update : $Date$ - * By : $Author$ - */ -public class ValidationError implements Serializable { - - /** serialVersionUID. */ - private static final long serialVersionUID = -1806823191454701123L; - - /** Category de l'erreur. */ - protected Category category; - - /** Niveau de l'erreur (fatal, error, warning, info). */ - protected ValidationLevel level; - - /** Message explicitant l'erreur de validation. */ - protected String message; - - /** Message donnant plus de précision sur l'erreur. */ - protected String detailMessage; - - /** Tip message (can be {@code null}). */ - protected String tipMessage; - - /** - * Numero des lignes dans le fichier CSV. - * - * Peut être vide si l'erreur n'est pas associé à une ligne specifiques. - */ - protected Set<String> lineNumbers; - - public ValidationError() { - super(); - - lineNumbers = new HashSet<String>(); - } - - /** - * Category de l'erreur. - * - * Peut etre null si l'erreur n'est pas associées à une category en particulier. - * - * @return category de l'erreur - */ - public Category getCategory() { - return category; - } - - /** - * Category de l'erreur. - * - * @param category category de l'erreur - */ - public void setCategory(Category category) { - this.category = category; - } - - /** - * Niveau de l'erreur (fatal, error, warning, info). - * - * @return level - */ - public ValidationLevel getLevel() { - return level; - } - - /** - * Niveau de l'erreur (fatal, error, warning, info). - * - * @param level level - */ - public void setLevel(ValidationLevel level) { - this.level = level; - } - - /** - * Message explicitant l'erreur de validation. - * - * @return message - */ - public String getMessage() { - return message; - } - - /** - * Message explicitant l'erreur de validation. - * - * @param message message - */ - public void setMessage(String message) { - this.message = message; - } - - /** - * Message donnant plus de précision sur l'erreur. - * - * Si le message de detail est null, le message est retourné. - * - * @return detail message - */ - public String getDetailMessage() { - String result = detailMessage; - if (result == null) { - result = message; - } - return result; - } - - /** - * Message donnant plus de précision sur l'erreur. - * - * @param detailMessage detail message - */ - public void setDetailMessage(String detailMessage) { - this.detailMessage = detailMessage; - } - - /** - * Get tip message (can be {@code null}). - * - * @return tip message - */ - public String getTipMessage() { - return tipMessage; - } - - /** - * Set tip message (can be {@code null}). - * - * @param tipMessage tip message - */ - public void setTipMessage(String tipMessage) { - this.tipMessage = tipMessage; - } - - /** - * Numero des lignes dans le fichier CSV. - * - * Peut être vide si l'erreur n'est pas associé a une ligne specifiques. - * - * @return line number - */ - public Set<String> getLineNumbers() { - return lineNumbers; - } - - /** - * Numero des lignes dans le fichier CSV. - * - * Peut être vide si l'erreur n'est pas associé a une ligne specifiques. - * - * @param lineNumber line number - */ - public void addLineNumber(String lineNumber) { - lineNumbers.add(lineNumber); - } - - /** - * Numero des lignes dans le fichier CSV. - * - * @param lineNumbers line numbers to add - */ - public void addAllLineNumber(Collection<String> lineNumbers) { - this.lineNumbers.addAll(lineNumbers); - } - - @Override - public String toString() { - return "ValidationError [level=" + level + ", message=" + message - + ", lineNumber=" + lineNumbers + "]"; - } -} Copied: trunk/coser-business/src/main/java/fr/ifremer/coser/services/ControlService.java (from rev 283, trunk/coser-business/src/main/java/fr/ifremer/coser/services/ValidationService.java) =================================================================== --- trunk/coser-business/src/main/java/fr/ifremer/coser/services/ControlService.java (rev 0) +++ trunk/coser-business/src/main/java/fr/ifremer/coser/services/ControlService.java 2010-11-26 14:03:54 UTC (rev 292) @@ -0,0 +1,975 @@ +/* + * #%L + * + * + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2010 Ifremer, Codelutin, Chatellier Eric + * %% + * 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 + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package fr.ifremer.coser.services; + +import static org.nuiton.i18n.I18n._; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang.xwork.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import com.opensymphony.xwork2.ActionContext; +import com.opensymphony.xwork2.ValidationAwareSupport; +import com.opensymphony.xwork2.config.Configuration; +import com.opensymphony.xwork2.config.ConfigurationManager; +import com.opensymphony.xwork2.inject.Container; +import com.opensymphony.xwork2.util.FileManager; +import com.opensymphony.xwork2.util.ValueStack; +import com.opensymphony.xwork2.util.ValueStackFactory; +import com.opensymphony.xwork2.validator.ActionValidatorManager; +import com.opensymphony.xwork2.validator.DelegatingValidatorContext; +import com.opensymphony.xwork2.validator.ValidationException; + +import fr.ifremer.coser.CoserBusinessConfig; +import fr.ifremer.coser.CoserConstants.Category; +import fr.ifremer.coser.CoserConstants.ValidationLevel; +import fr.ifremer.coser.bean.Control; +import fr.ifremer.coser.bean.Project; +import fr.ifremer.coser.control.DiffCatchLengthControlError; +import fr.ifremer.coser.control.ProgressMonitor; +import fr.ifremer.coser.control.ControlError; +import fr.ifremer.coser.data.AbstractDataEntity; +import fr.ifremer.coser.data.Catch; +import fr.ifremer.coser.data.Haul; +import fr.ifremer.coser.data.Length; +import fr.ifremer.coser.data.Strata; +import fr.ifremer.coser.storage.DataStorage; + +/** + * Validation service. + * + * @author chatellier + * @version $Revision$ + * + * Last update : $Date$ + * By : $Author$ + */ +public class ControlService { + + private static final Log log = LogFactory.getLog(ControlService.class); + + protected CoserBusinessConfig config; + + protected ValidationAwareSupport validationSupport; + + protected DelegatingValidatorContext validationContext; + + protected ActionValidatorManager validator; + + protected ActionContext context; + + /** + * Initialise le context xworks. + * + * @param config configuration + */ + public ControlService(CoserBusinessConfig config) { + this.config = config; + validationSupport = new ValidationAwareSupport(); + validationContext = new DelegatingValidatorContext(validationSupport); + + ConfigurationManager confManager = new ConfigurationManager(); + Configuration conf = confManager.getConfiguration(); + Container container = conf.getContainer(); + ValueStackFactory stackFactory = container.getInstance(ValueStackFactory.class); + ValueStack vs = stackFactory.createValueStack(); + + context = new ActionContext(vs.getContext()); + ActionContext.setContext(context); + + // huge improve cache performance + FileManager.setReloadingConfigs(false); + + // init validator + Container container2 = context.getContainer(); + validator = container2.getInstance(ActionValidatorManager.class, "no-annotations"); + } + + /** + * Valide un seul bean, retourne la liste des erreurs trouvées. + * + * @param bean bean to validate + * @param category result errors category + * @return + */ + protected List<ControlError> validate(AbstractDataEntity bean, Category category) { + List<ControlError> result = new ArrayList<ControlError>();; + try { + // obligatoire pour les appel dans les thread et swing EDT + ActionContext.setContext(context); + + for(ValidationLevel validationLevel : ValidationLevel.values()) { + + try { + validator.validate(bean, validationLevel.getXWorkContext(), validationContext); + + if (validationContext.hasErrors()) { + Map<String, List<String>> fieldErrors = validationContext.getFieldErrors(); + + for (List<String> errors : fieldErrors.values()) { + ControlError error = new ControlError(); + error.setCategory(category); + String messages = StringUtils.join(errors, ", "); + error.setMessage(messages); + error.setLevel(validationLevel); + + // add error line + error.addLineNumber(bean.getLine()); + + result.add(error); + } + + Collection<String> actionMessages = validationContext.getActionErrors(); + if (CollectionUtils.isNotEmpty(actionMessages)) { + ControlError error = new ControlError(); + error.setCategory(category); + String messages = StringUtils.join(actionMessages, ", "); + error.setMessage(messages); + error.setLevel(validationLevel); + + // add error line + error.addLineNumber(bean.getLine()); + + result.add(error); + } + } + } + finally { + // vidage par log level + validationSupport.clearFieldErrors(); + validationSupport.clearActionErrors(); + } + } + + } catch (ValidationException ex) { + if (log.isErrorEnabled()) { + log.error("Can't validate bean", ex); + } + } + finally { + validationSupport.clearFieldErrors(); + validationSupport.clearActionErrors(); + } + return result; + } + + /** + * Valide toutes les données du projet. + * + * @param project project + * @param control control a valider + * @param progress progress monitor + * @return les erreurs de validation + */ + public List<ControlError> validateData(Project project, Control control, ProgressMonitor progress) { + + // valide chaque category + List<ControlError> validationErrors = new ArrayList<ControlError>(); + for (Category category : Category.values()) { + if (category.isDataCategory()) { + // validation de la category seule (generique) + List<ControlError> categoryErrors = validateCategoryXWork(control, category, progress); + if (categoryErrors != null) { + validationErrors.addAll(categoryErrors); + } + // validation specifique de la category + List<ControlError> specificErrors = validateCategorySpecific(control, category, progress); + if (specificErrors != null) { + validationErrors.addAll(specificErrors); + } + } + } + + // validation entre catch et length (specific) + List<ControlError> diffCatchLengthErrors = validateDiffCatchLength(control); + if (diffCatchLengthErrors != null) { + validationErrors.addAll(diffCatchLengthErrors); + } + + // validation par croisement de fichiers + List<ControlError> crossFileErrors = validationCrossFiles(project, control, progress); + if (crossFileErrors != null) { + validationErrors.addAll(crossFileErrors); + } + + // cas particulier, s'il n'y a aucune erreur, on ajout une erreur de + // type info pour dire qu'il n'y a pas d'erreur + if (validationErrors.isEmpty()) { + ControlError noErrorError = new ControlError(); + noErrorError.setLevel(ValidationLevel.INFO); + noErrorError.setMessage(_("coser.business.control.noerrorfound")); + validationErrors.add(noErrorError); + } + return validationErrors; + } + + /** + * Valide une category entière d'un project. + * + * @param control control a valider + * @param category category a valider + * @param progress progress monitor + * @return les erreurs de validation + */ + public List<ControlError> validateCategory(Control control, Category category, ProgressMonitor progress) { + + // validation de la category seule (generique) + List<ControlError> errors = validateCategoryXWork(control, category, progress); + // validation specifique de la category + List<ControlError> specificErrors = validateCategorySpecific(control, category, progress); + if (specificErrors != null) { + errors.addAll(specificErrors); + } + + return errors; + } + + /** + * Valide une category entière d'un project. + * + * @param control control a valider + * @param category category a valider + * @param progress progress monitor + * @return les erreurs de validation (not null) + */ + public List<ControlError> validateCategoryXWork(Control control, Category category, ProgressMonitor progress) { + + // instance des bean utilisé lors de la validation + Catch beanCatch = new Catch(); + Haul beanHaul = new Haul(); + Length beanLength = new Length(); + Strata beanStrata = new Strata(); + + // get content depending on category + DataStorage dataToCheck = null; + switch (category) { + case CATCH: + dataToCheck = control.getCatch(); + break; + case HAUL: + dataToCheck = control.getHaul(); + break; + case LENGTH: + dataToCheck = control.getLength(); + break; + case STRATA: + dataToCheck = control.getStrata(); + break; + } + + // update progress + int total = dataToCheck.size() - 1; + if (progress != null) { + progress.setText(_("coser.business.control.step.xworks", _(category.getTranslationKey()), 0)); + progress.setTotal(total); + } + + Map<String, String> uniqueDataKeys = new HashMap<String, String>(); + List<ControlError> validationErrors = new ArrayList<ControlError>(); + Iterator<String[]> itDataToCheck = dataToCheck.iterator(); + int lineIndex = 1; // 1 = skip header + itDataToCheck.next(); // skip header + while (itDataToCheck.hasNext()) { + + // update progress + if (progress != null) { + int progressPercent = (int)((double)lineIndex / (double)total * 100.0); + progress.setText(_("coser.business.control.step.xworks", progressPercent)); + progress.setCurrent(lineIndex); + ++lineIndex; + } + + // first, do single bean validation + String[] line = itDataToCheck.next(); + List<ControlError> errors = null; + switch (category) { + case CATCH: + beanCatch.setData(line); + errors = validate(beanCatch, category); + break; + case HAUL: + beanHaul.setData(line); + errors = validate(beanHaul, category); + break; + case LENGTH: + beanLength.setData(line); + errors = validate(beanLength, category); + break; + case STRATA: + beanStrata.setData(line); + errors = validate(beanStrata, category); + break; + } + if (errors != null) { + validationErrors.addAll(errors); + } + + // check for duplicated lines + String lineNumber = line[AbstractDataEntity.INDEX_LINE]; + String uniqueDataKey = getSignificantData(category, line); + if (uniqueDataKeys.containsKey(uniqueDataKey)) { + ControlError error = new ControlError(); + error.setCategory(category); + error.setLevel(ValidationLevel.ERROR); + error.addLineNumber(uniqueDataKeys.get(uniqueDataKey)); + error.addLineNumber(lineNumber); + error.setMessage(_("coser.business.control.error.duplicatedLine", uniqueDataKey)); + error.setDetailMessage(_("coser.business.control.error.duplicatedLineDetails", uniqueDataKey)); + validationErrors.add(error); + } + else { + uniqueDataKeys.put(uniqueDataKey, lineNumber); + } + } + + return validationErrors; + } + + /** + * Retourne sous forme de clé la partie significative des données. + * Donc sans le numéro de ligne suivant la catégorie. + * + * Utilisé pour la detection des doublons. + * + * CAPTURES + * Vérifier l'unicité sur : "Campagne", "Annee", "Trait", "Espece" + * + * STRATES + * Vérifier l'unicité sur : "Campagne", "Strate" + * + * TAILLES + * Vérifier l'unicité sur : Campagne", "Annee", "Trait", "Espece", "Sexe", "Maturite", "Longueur" + * + * TRAITS + * Vérifier l'unicité sur : "Campagne", "Annee", "Trait", "Mois" + * + * @param category category + * @param data data + * @return string key + */ + protected String getSignificantData(Category category, String[] data) { + StringBuilder sb = new StringBuilder(); + for (int index = 0 ; index < data.length ; ++index) { + // never count index line + if (index != AbstractDataEntity.INDEX_LINE) { + switch (category) { + case CATCH: + if (index == Catch.INDEX_SURVEY || index == Catch.INDEX_YEAR || index == Catch.INDEX_HAUL || index == Catch.INDEX_SPECIES) { + sb.append(data[index]).append('|'); + } + break; + case HAUL: + if (index == Haul.INDEX_SURVEY || index == Haul.INDEX_YEAR || index == Haul.INDEX_HAUL || index == Haul.INDEX_MONTH) { + sb.append(data[index]).append('|'); + } + break; + case LENGTH: + if (index == Length.INDEX_SURVEY || index == Length.INDEX_YEAR || index == Length.INDEX_HAUL || + index == Length.INDEX_SPECIES || index == Length.INDEX_SEX || index == Length.INDEX_MATURITY || index == Length.INDEX_LENGTH) { + sb.append(data[index]).append('|'); + } + break; + case STRATA: + if (index == Strata.INDEX_SURVEY || index == Strata.INDEX_STRATUM) { + sb.append(data[index]).append('|'); + } + break; + } + + } + + } + return sb.toString(); + } + + /** + * Effectue un calcul global, mais specific a chaque categorie. + * + * @param control control + * @param category category + * @param progress progress monitor + * @return + */ + protected List<ControlError> validateCategorySpecific(Control control, + Category category, ProgressMonitor progress) { + + List<ControlError> validationErrors = null; + switch (category) { + case CATCH: + validationErrors = validateCategorySpecificCatch(control, progress); + break; + case HAUL: + validationErrors = validateCategorySpecificHaul(control, progress); + break; + case LENGTH: + validationErrors = validateCategorySpecificLength(control, progress); + break; + case STRATA: + validationErrors = validateCategorySpecificStrata(control, progress); + break; + } + return validationErrors; + } + + /** + * Alerte si Somme(CAPTURES$Nombre par CAPTURES$Annee|Strate|Espece) < nobsmin. + * Erreur si CAPT$Nombre n'est pas 0 si le poids dans CAPT$Poids>0 + * + * @param control + * @param progress + * @return + */ + protected List<ControlError> validateCategorySpecificCatch( + Control control, ProgressMonitor progress) { + + List<ControlError> validationErrors = new ArrayList<ControlError>(); + + int total = control.getCatch().size() - 1; + if (progress != null) { + progress.setText(_("coser.business.control.step.observation", 0)); + progress.setTotal(total); + } + + // "Campagne","Annee","Trait","Espece","Nombre","Poids" + // regroupement par campagne/annee/trait + Map<String, Double> nombreForKey = new HashMap<String, Double>(); + Map<String, String> firstLineForKey = new HashMap<String, String>(); + + // parcours des elements + Iterator<String[]> itTuple = control.getCatch().iterator(); + itTuple.next(); // skip header + int lineIndex = 1; // skip header + while (itTuple.hasNext()) { + String[] tuple = itTuple.next(); + + // update progress + if (progress != null) { + int progressPercent = (int)((double)lineIndex / (double)total * 100.0); + progress.setText(_("coser.business.control.step.observation", progressPercent)); + progress.setCurrent(lineIndex); + ++lineIndex; + } + + // compute key + StringBuffer sb = new StringBuffer(); + for (int tupleIndex = 0 ; tupleIndex < tuple.length; ++tupleIndex) { + if (tupleIndex == Catch.INDEX_YEAR || tupleIndex == Catch.INDEX_HAUL || + tupleIndex == Catch.INDEX_SPECIES) { + sb.append(tuple[tupleIndex]).append(';'); + } + } + + // store number sum + String nombreValue = tuple[Catch.INDEX_NUMBER]; + String lineNumber = tuple[AbstractDataEntity.INDEX_LINE]; + try { + Double nombre = Double.valueOf(nombreValue); + String key = sb.toString(); + if (nombreForKey.containsKey(key)) { + Double oldValue = nombreForKey.get(key); + Double newValue = oldValue + nombre; + nombreForKey.put(key, newValue); + } + else { + nombreForKey.put(key, nombre); + // sauvegarde la premiere ligne qui correspond a un regroupement de clé + firstLineForKey.put(key, lineNumber); + } + } + catch (NumberFormatException ex) { + // par trop grave, normalement les données deviennent + // valide au fil de la validation + if (log.isWarnEnabled()) { + log.warn("Can't parse '" + nombreValue + "' as double"); + } + } + } + + // now look for invalid data + for (Map.Entry<String, Double> sumObservation : nombreForKey.entrySet()) { + String key = sumObservation.getKey(); + Double value = sumObservation.getValue(); + if (value < config.getControlNobsmin()) { + + String lineNumber = firstLineForKey.get(key); + + ControlError error = new ControlError(); + error.setCategory(Category.CATCH); + error.setLevel(ValidationLevel.WARNING); + error.addLineNumber(lineNumber); + error.setMessage(_("coser.business.control.error.minObservationCount")); + error.setDetailMessage(_("coser.business.control.error.minObservationCountDetail", key, value)); + validationErrors.add(error); + } + } + + return validationErrors; + } + + /** + * Detecte des différences entre les nombres dans captures + * et les nombres dand taille. + * + * @see PublicationService#getCompareCatchLengthGraph(Project, Control, String) for details + * @see CoserBusinessConfig#getControlDiffCatchLength() for option + * + * @return errors + */ + protected List<ControlError> validateDiffCatchLength(Control control) { + + List<ControlError> validationErrors = new ArrayList<ControlError>(); + SortedSet<String> setYear = new TreeSet<String>(); + + // look for data (data summed over catch) + Map<String, Map<String, Double>> catchForSpeciesYears = new HashMap<String, Map<String, Double>>(); + Iterator<String[]> itCatchData = control.getCatch().iterator(); + itCatchData.next(); // header + while (itCatchData.hasNext()) { + String[] tuple = itCatchData.next(); + String species = tuple[Catch.INDEX_SPECIES]; + + Map<String, Double> speciesCatchForYears = catchForSpeciesYears.get(species); + if (speciesCatchForYears == null) { + speciesCatchForYears = new HashMap<String, Double>(); + catchForSpeciesYears.put(species, speciesCatchForYears); + } + + String year = tuple[Catch.INDEX_YEAR]; + setYear.add(year); + String nombreValue = tuple[Catch.INDEX_NUMBER]; + try { + Double nombreDouble = Double.valueOf(nombreValue); + + if (speciesCatchForYears.containsKey(year)) { + Double oldValue = speciesCatchForYears.get(year); + Double newValue = oldValue + nombreDouble; + speciesCatchForYears.put(year, newValue); + } + else { + speciesCatchForYears.put(year, nombreDouble); + } + } + catch (NumberFormatException ex) { + if (log.isWarnEnabled()) { + log.warn("Can't parse " + nombreValue + " as double"); + } + } + } + + // look for data (data summed over length) + Map<String, Map<String, Double>> lengthForSpeciesYears = new HashMap<String, Map<String, Double>>(); + Iterator<String[]> itLengthData = control.getLength().iterator(); + itLengthData.next(); // header + while (itLengthData.hasNext()) { + String[] tuple = itLengthData.next(); + String species = tuple[Length.INDEX_SPECIES]; + + Map<String, Double> speciesLengthForYears = lengthForSpeciesYears.get(species); + if (speciesLengthForYears == null) { + speciesLengthForYears = new HashMap<String, Double>(); + lengthForSpeciesYears.put(species, speciesLengthForYears); + } + + String year = tuple[Length.INDEX_YEAR]; + setYear.add(year); + String nombreValue = tuple[Length.INDEX_NUMBER]; + try { + Double nombreDouble = Double.valueOf(nombreValue); + + if (speciesLengthForYears.containsKey(year)) { + Double oldValue = speciesLengthForYears.get(year); + Double newValue = oldValue + nombreDouble; + speciesLengthForYears.put(year, newValue); + } + else { + speciesLengthForYears.put(year, nombreDouble); + } + } + catch (NumberFormatException ex) { + if (log.isWarnEnabled()) { + log.warn("Can't parse " + nombreValue + " as double"); + } + } + } + + // check for all species and years + for (Map.Entry<String, Map<String, Double>> catchEntries : catchForSpeciesYears.entrySet()) { + String species = catchEntries.getKey(); + Map<String, Double> catchNumbers = catchEntries.getValue(); + Map<String, Double> lengthNumbers = lengthForSpeciesYears.get(species); + + if (lengthNumbers == null) { + // on suppose que cette erreur est détectée par un autre controle + continue; + } + + Iterator<String> itYears = setYear.iterator(); + while (itYears.hasNext()) { + String year = itYears.next(); + + Double catchNumber = catchNumbers.get(year); + Double lengthNumber = lengthNumbers.get(year); + if (catchNumber == null) { + catchNumber = 0.0; // marche pou NA + } + if (lengthNumber == null) { + lengthNumber = 0.0; // marche pour NA + } + + // diff entre 4 et 5 = (5-4) * 100 / 5 + double min = Math.min(catchNumber, lengthNumber); + // si c'est 0, ou absence de catures, ce n'est pas une erreur + if (min > 0) { + double max = Math.max(catchNumber, lengthNumber); + double diff = (max - min) * 100 / max; + + if (diff > config.getControlDiffCatchLength()) { + DiffCatchLengthControlError error = new DiffCatchLengthControlError(); + error.setSpecies(species); + error.setLevel(ValidationLevel.WARNING); + error.setMessage(_("coser.business.control.error.diffCatchLength")); + error.setDetailMessage(_("coser.business.control.error.diffCatchLengthDetail", species, year)); + validationErrors.add(error); + } + } + } + } + + return validationErrors; + } + + /** + * Alerte si Somme(TAILLES$Nombre par TAILLES$Annee|Strate|Espece) < nobsmin + * + * @param control + * @param progress + * @return + */ + protected List<ControlError> validateCategorySpecificLength( + Control control, ProgressMonitor progress) { + List<ControlError> validationErrors = new ArrayList<ControlError>(); + + int total = control.getLength().size() - 1; + if (progress != null) { + progress.setText(_("coser.business.control.step.observation", 0)); + progress.setTotal(total); + } + + // "Campagne","Annee","Trait","Espece","Nombre","Poids" + // regroupement par campagne/annee/trait + Map<String, Double> nombreForKey = new HashMap<String, Double>(); + Map<String, String> firstLineForKey = new HashMap<String, String>(); + + // parcours des elements + Iterator<String[]> itTuple = control.getLength().iterator(); + itTuple.next(); // skip header + int lineIndex = 1; // skip header + while (itTuple.hasNext()) { + String[] tuple = itTuple.next(); + + // update progress + if (progress != null) { + int progressPercent = (int)((double)lineIndex / (double)total * 100.0); + progress.setText(_("coser.business.control.step.observation", progressPercent)); + progress.setCurrent(lineIndex); + ++lineIndex; + } + + // compute key + StringBuffer sb = new StringBuffer(); + for (int tupleIndex = 0 ; tupleIndex < tuple.length; ++tupleIndex) { + if (tupleIndex == Length.INDEX_YEAR || tupleIndex == Length.INDEX_HAUL || + tupleIndex == Length.INDEX_SPECIES) { + sb.append(tuple[tupleIndex]).append(';'); + } + } + + // store number sum + String nombreValue = tuple[Length.INDEX_NUMBER]; + String lineNumber = tuple[AbstractDataEntity.INDEX_LINE]; + try { + Double nombre = Double.valueOf(nombreValue); + String key = sb.toString(); + if (nombreForKey.containsKey(key)) { + Double oldValue = nombreForKey.get(key); + Double newValue = oldValue + nombre; + nombreForKey.put(key, newValue); + } + else { + nombreForKey.put(key, nombre); + // sauvegarde la premiere ligne qui correspond a un regroupement de clé + firstLineForKey.put(key, lineNumber); + } + } + catch (NumberFormatException ex) { + // par trop grave, normalement les données deviennet + // valide au fil de la validation + if (log.isWarnEnabled()) { + log.warn("Can't parse " + nombreValue + " as double"); + } + } + } + + // now look for invalid data + for (Map.Entry<String, Double> sumObservation : nombreForKey.entrySet()) { + String key = sumObservation.getKey(); + Double value = sumObservation.getValue(); + if (value < config.getControlNobsmin()) { + + String lineNumber = firstLineForKey.get(key); + + ControlError error = new ControlError(); + error.setCategory(Category.LENGTH); + error.setLevel(ValidationLevel.WARNING); + error.addLineNumber(lineNumber); + error.setMessage(_("coser.business.control.error.minObservationCount")); + error.setDetailMessage(_("coser.business.control.error.minObservationCountDetail", key, value)); + validationErrors.add(error); + } + } + + return validationErrors; + } + + /** + * @param control + * @param progress + * @return + */ + protected List<ControlError> validateCategorySpecificHaul(Control control, + ProgressMonitor progress) { + return null; + } + + /** + * @param control + * @param progress + * @return + */ + protected List<ControlError> validateCategorySpecificStrata( + Control control, ProgressMonitor progress) { + return null; + } + + /** + * Validation par croisement de fichiers. + * + * Les erreurs relevé ici porte sur plusieurs fichiers. + * + * @param control control + * @param progress progress + * @return error list + */ + protected List<ControlError> validationCrossFiles(Project project, Control control, + ProgressMonitor progress) { + + if (progress != null) { + progress.setText(_("coser.business.control.step.crossFileChech", 0)); + progress.setTotal(100); + progress.setCurrent(0); + } + + List<ControlError> crossFilesErrors = new ArrayList<ControlError>(); + + // get all iterators + Iterator<String[]> itReftax = project.getRefTaxSpecies().iterator(); + itReftax.next(); // skip header + Iterator<String[]> itCatch = control.getCatch().iterator(); + itCatch.next(); // skip header + Iterator<String[]> itLength = control.getLength().iterator(); + itLength.next(); // skip header + Iterator<String[]> itStrata = control.getStrata().iterator(); + itStrata.next(); // skip header + Iterator<String[]> itHaul = control.getHaul().iterator(); + itHaul.next(); // skip header + + // declare all necessary data + Set<String> refTaxSpecies = new HashSet<String>(); + Set<String> catchYear = new HashSet<String>(); + Set<String> lengthYear = new HashSet<String>(); + Set<String> haulYear = new HashSet<String>(); + Set<String> campagneNames = new HashSet<String>(); + Set<String> lengthYearHaulSpecies = new HashSet<String>(); + Set<String> catchYearHaulSpecies = new HashSet<String>(); + Set<String> haulYearHaul = new HashSet<String>(); + Set<String> lengthYearHaul = new HashSet<String>(); + Set<String> catchYearHaul = new HashSet<String>(); + + // parcourt de toutes les données + while (itReftax.hasNext() ) { + String[] tuple = itReftax.next(); + // "C_Perm","NumSys","NivSys","C_VALIDE","L_VALIDE","AA_VALIDE","C_TxP\u00E8re","Taxa" + refTaxSpecies.add(tuple[3]); + } + + while (itCatch.hasNext()) { + String[] catchData = itCatch.next(); + campagneNames.add(catchData[Catch.INDEX_SURVEY]); + catchYear.add(catchData[Catch.INDEX_YEAR]); + catchYearHaulSpecies.add(catchData[Catch.INDEX_YEAR] + "|" + catchData[Catch.INDEX_HAUL] + "|" + catchData[Catch.INDEX_SPECIES]); + catchYearHaul.add(catchData[Catch.INDEX_YEAR] + "|" + catchData[Catch.INDEX_HAUL]); + + // Contrôle des noms d'espèces dans fichiers CAPTURES et TAILLES qui + // doivent être dans le référentiel "reftax": CAPTURES$Espece et + // TAILLES$Espece doivent exister dans REFTAX$ C_VALIDE + if (!refTaxSpecies.contains(catchData[Catch.INDEX_SPECIES])) { + ControlError error = new ControlError(); + error.setCategory(Category.CATCH); + error.addLineNumber(catchData[Catch.INDEX_LINE]); + error.setLevel(ValidationLevel.ERROR); + error.setMessage(_("coser.business.control.error.nonExistantSpecies")); + error.setDetailMessage(_("coser.business.control.error.nonExistantSpeciesDetail", catchData[Catch.INDEX_SPECIES])); + crossFilesErrors.add(error); + } + } + + if (progress != null) { + progress.setText(_("coser.business.control.step.crossFileChech", 25)); + progress.setCurrent(25); + } + + while (itLength.hasNext()) { + String[] lengthData = itLength.next(); + campagneNames.add(lengthData[Length.INDEX_SURVEY]); + lengthYear.add(lengthData[Length.INDEX_YEAR]); + lengthYearHaulSpecies.add(lengthData[Length.INDEX_YEAR] + "|" + + lengthData[Length.INDEX_HAUL] + "|" + lengthData[Length.INDEX_SPECIES]); + lengthYearHaul.add(lengthData[Length.INDEX_YEAR] + "|" + lengthData[Length.INDEX_HAUL]); + + // Contrôle des noms d'espèces dans fichiers CAPTURES et TAILLES qui + // doivent être dans le référentiel "reftax": CAPTURES$Espece et + // TAILLES$Espece doivent exister dans REFTAX$ C_VALIDE + if (!refTaxSpecies.contains(lengthData[Length.INDEX_SPECIES])) { + ControlError error = new ControlError(); + error.setCategory(Category.LENGTH); + error.addLineNumber(lengthData[Length.INDEX_LINE]); + error.setLevel(ValidationLevel.ERROR); + error.setMessage(_("coser.business.control.error.nonExistantSpecies")); + error.setDetailMessage(_("coser.business.control.error.nonExistantSpeciesDetail", lengthData[Length.INDEX_SPECIES])); + crossFilesErrors.add(error); + } + } + + if (progress != null) { + progress.setText(_("coser.business.control.step.crossFileChech", 50)); + progress.setCurrent(50); + } + + while (itStrata.hasNext()) { + String[] strataData = itStrata.next(); + campagneNames.add(strataData[Strata.INDEX_SURVEY]); + } + + if (progress != null) { + progress.setText(_("coser.business.control.step.crossFileChech", 75)); + progress.setCurrent(75); + } + + while (itHaul.hasNext()) { + String[] haulData = itHaul.next(); + campagneNames.add(haulData[Haul.INDEX_SURVEY]); + haulYear.add(haulData[Haul.INDEX_YEAR]); + haulYearHaul.add(haulData[Haul.INDEX_YEAR] + "|" + haulData[Haul.INDEX_HAUL]); + } + + // Vérifier que les mêmes années sont présentes dans fichiers captures + // traits et tailles: CAPTURES$Annee, TRAITS$Annee, TAILLES$Annee. + if (!catchYear.equals(lengthYear) || !haulYear.equals(lengthYear)) { + ControlError error = new ControlError(); + error.setLevel(ValidationLevel.FATAL); + error.setMessage(_("coser.business.control.error.yearsNotEquals")); + //error.setDetailMessage(_("coser.business.control.error.yearsNotEquals")); + crossFilesErrors.add(error); + } + + // Vérifier que le nom de la campagne est le même dans tous les fichiers *$Campagne. + if (campagneNames.size() != 1) { + ControlError error = new ControlError(); + error.setLevel(ValidationLevel.FATAL); + error.setMessage(_("coser.business.control.error.surveyNotEquals")); + //error.setDetailMessage(_("coser.business.control.error.surveyNotEquals")); + crossFilesErrors.add(error); + } + + // Vérifier que pour chaque espèce présent dans un trait d'une année + // dans le fichier tailles (noté Annee|Trait|Espece) il y a une donnée + // dans le fichier captures (le contraire n'est pas vraie) + Collection<String> missingSpeciesCatchTuples = CollectionUtils.subtract(lengthYearHaulSpecies, catchYearHaulSpecies); + for (String missingSpeciesCatchTuple : missingSpeciesCatchTuples) { + ControlError error = new ControlError(); + error.setLevel(ValidationLevel.FATAL); + error.setMessage(_("coser.business.control.error.missingYearHaulSpeciesForCatchData")); + error.setDetailMessage(_("coser.business.control.error.missingYearHaulSpeciesForCatchDataDetail", missingSpeciesCatchTuple)); + error.setTipMessage(_("coser.business.control.error.missingYearHaulSpeciesForCatchDataTip")); + crossFilesErrors.add(error); + } + + // Vérifier que pour chaque trait présent une année (noté Annee|Trait) + // dans les fichiers tailles et capt il y a une donnée dans le fichier + // traits + Collection<String> missingHaulLengthTuples = CollectionUtils.subtract(lengthYearHaul, haulYearHaul); + for (String missingHaulLengthTuple : missingHaulLengthTuples) { + ControlError error = new ControlError(); + error.setLevel(ValidationLevel.FATAL); + error.setMessage(_("coser.business.control.error.missingYearHaulForLengthData")); + error.setDetailMessage(_("coser.business.control.error.missingYearHaulForLengthDataDetail", missingHaulLengthTuple)); + error.setTipMessage(_("coser.business.control.error.missingYearHaulForLengthDataTip")); + crossFilesErrors.add(error); + } + + // Vérifier que pour chaque trait présent une année (noté Annee|Trait) + // dans les fichiers tailles et capt il y a une donnée dans le fichier + // traits + Collection<String> missingHaulCatchTuples = CollectionUtils.subtract(catchYearHaul, haulYearHaul); + for (String missingHaulCatchTuple : missingHaulCatchTuples) { + ControlError error = new ControlError(); + error.setLevel(ValidationLevel.FATAL); + error.setMessage(_("coser.business.control.error.missingYearHaulForCatchData")); + error.setDetailMessage(_("coser.business.control.error.missingYearHaulForCatchDataDetail", missingHaulCatchTuple)); + error.setTipMessage(_("coser.business.control.error.missingYearHaulForCatchDataTip")); + crossFilesErrors.add(error); + } + + if (progress != null) { + progress.setText(_("coser.business.control.step.crossFileChech", 100)); + progress.setCurrent(100); + } + + return crossFilesErrors; + } +} Modified: trunk/coser-business/src/main/java/fr/ifremer/coser/services/PublicationService.java =================================================================== --- trunk/coser-business/src/main/java/fr/ifremer/coser/services/PublicationService.java 2010-11-26 13:47:45 UTC (rev 291) +++ trunk/coser-business/src/main/java/fr/ifremer/coser/services/PublicationService.java 2010-11-26 14:03:54 UTC (rev 292) @@ -65,9 +65,9 @@ import fr.ifremer.coser.CoserConstants.Category; import fr.ifremer.coser.bean.AbstractDataContainer; import fr.ifremer.coser.bean.Project; -import fr.ifremer.coser.control.DiffCatchLengthValidationError; -import fr.ifremer.coser.control.GlobalValidationGroup; -import fr.ifremer.coser.control.ValidationError; +import fr.ifremer.coser.control.DiffCatchLengthControlError; +import fr.ifremer.coser.control.ControlErrorGroup; +import fr.ifremer.coser.control.ControlError; import fr.ifremer.coser.data.Catch; import fr.ifremer.coser.data.Length; @@ -270,7 +270,7 @@ * @param validationErrors errors list * @throws CoserBusinessException */ - public File exportErrorsAsHTML(Project project, AbstractDataContainer container, List<ValidationError> validationErrors) throws CoserBusinessException { + public File exportErrorsAsHTML(Project project, AbstractDataContainer container, List<ControlError> validationErrors) throws CoserBusinessException { File exportHtmlFile = null; PrintStream out = null; @@ -290,26 +290,26 @@ out.println("</head><body>"); // convert all errors as tree (code duplicated from ui tree model) - Map<String, List<GlobalValidationGroup>> validationCategoryChild = new HashMap<String, List<GlobalValidationGroup>>(); - Map<GlobalValidationGroup, List<ValidationError>> validationErrorsChilds = new HashMap<GlobalValidationGroup, List<ValidationError>>(); - for (ValidationError validationError : validationErrors) { + Map<String, List<ControlErrorGroup>> validationCategoryChild = new HashMap<String, List<ControlErrorGroup>>(); + Map<ControlErrorGroup, List<ControlError>> validationErrorsChilds = new HashMap<ControlErrorGroup, List<ControlError>>(); + for (ControlError validationError : validationErrors) { String category = validationError.getCategory() == null ? _("coser.business.control.error.allCategories") : _(validationError.getCategory().getTranslationKey()); - List<GlobalValidationGroup> errorGroup = validationCategoryChild.get(category); + List<ControlErrorGroup> errorGroup = validationCategoryChild.get(category); if (errorGroup == null) { - errorGroup = new ArrayList<GlobalValidationGroup>(); + errorGroup = new ArrayList<ControlErrorGroup>(); validationCategoryChild.put(category, errorGroup); } - GlobalValidationGroup group = new GlobalValidationGroup(validationError.getCategory(), validationError.getLevel(), validationError.getMessage()); - List<ValidationError> childErrors = validationErrorsChilds.get(group); + ControlErrorGroup group = new ControlErrorGroup(validationError.getCategory(), validationError.getLevel(), validationError.getMessage()); + List<ControlError> childErrors = validationErrorsChilds.get(group); if (childErrors == null) { - childErrors = new ArrayList<ValidationError>(); + childErrors = new ArrayList<ControlError>(); validationErrorsChilds.put(group, childErrors); errorGroup.add(group); } - if (validationError instanceof DiffCatchLengthValidationError) { - DiffCatchLengthValidationError diffError = (DiffCatchLengthValidationError)validationError; + if (validationError instanceof DiffCatchLengthControlError) { + DiffCatchLengthControlError diffError = (DiffCatchLengthControlError)validationError; speciesGraph.add(diffError.getSpecies()); } childErrors.add(validationError); @@ -319,11 +319,11 @@ // render output html out.println("<h1 style='text-align:center'>" + _("coser.business.publication.htmlexporttitle", project.getName()) + "</h1>"); - for (Map.Entry<String, List<GlobalValidationGroup>> categoriesEntry : validationCategoryChild.entrySet()) { + for (Map.Entry<String, List<ControlErrorGroup>> categoriesEntry : validationCategoryChild.entrySet()) { out.println("<h2>" + categoriesEntry.getKey() + "</h2>"); out.println("<ul>"); - for (GlobalValidationGroup group : categoriesEntry.getValue()) { + for (ControlErrorGroup group : categoriesEntry.getValue()) { out.println("<li style='margin-bottom:20px'><span style='color:"); switch (group.getValidationLevel()) { @@ -344,7 +344,7 @@ out.println("</span>"); out.println("<ul>"); - for (ValidationError error : validationErrorsChilds.get(group)) { + for (ControlError error : validationErrorsChilds.get(group)) { out.println("<li type='circle'>"); out.print(_(error.getDetailMessage())); Set<String> lineNumbers = error.getLineNumbers(); @@ -364,8 +364,8 @@ out.println("<p style='font-style:italic'>" + tipMessage + "</p>"); } // ajout d'un graph si necessaire - if (error instanceof DiffCatchLengthValidationError) { - DiffCatchLengthValidationError diffError = (DiffCatchLengthValidationError)error; + if (error instanceof DiffCatchLengthControlError) { + DiffCatchLengthControlError diffError = (DiffCatchLengthControlError)error; JFreeChart chart = charts.get(diffError.getSpecies()); File tmpChartImage = File.createTempFile("chart-", ".jpg", imageDirectory); ChartUtilities.saveChartAsJPEG(tmpChartImage, chart, 800, 400); Deleted: trunk/coser-business/src/main/java/fr/ifremer/coser/services/ValidationService.java =================================================================== --- trunk/coser-business/src/main/java/fr/ifremer/coser/services/ValidationService.java 2010-11-26 13:47:45 UTC (rev 291) +++ trunk/coser-business/src/main/java/fr/ifremer/coser/services/ValidationService.java 2010-11-26 14:03:54 UTC (rev 292) @@ -1,975 +0,0 @@ -/* - * #%L - * - * - * $Id$ - * $HeadURL$ - * %% - * Copyright (C) 2010 Ifremer, Codelutin, Chatellier Eric - * %% - * 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 - * <http://www.gnu.org/licenses/lgpl-3.0.html>. - * #L% - */ - -package fr.ifremer.coser.services; - -import static org.nuiton.i18n.I18n._; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.SortedSet; -import java.util.TreeSet; - -import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.lang.xwork.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import com.opensymphony.xwork2.ActionContext; -import com.opensymphony.xwork2.ValidationAwareSupport; -import com.opensymphony.xwork2.config.Configuration; -import com.opensymphony.xwork2.config.ConfigurationManager; -import com.opensymphony.xwork2.inject.Container; -import com.opensymphony.xwork2.util.FileManager; -import com.opensymphony.xwork2.util.ValueStack; -import com.opensymphony.xwork2.util.ValueStackFactory; -import com.opensymphony.xwork2.validator.ActionValidatorManager; -import com.opensymphony.xwork2.validator.DelegatingValidatorContext; -import com.opensymphony.xwork2.validator.ValidationException; - -import fr.ifremer.coser.CoserBusinessConfig; -import fr.ifremer.coser.CoserConstants.Category; -import fr.ifremer.coser.CoserConstants.ValidationLevel; -import fr.ifremer.coser.bean.Control; -import fr.ifremer.coser.bean.Project; -import fr.ifremer.coser.control.DiffCatchLengthValidationError; -import fr.ifremer.coser.control.ProgressMonitor; -import fr.ifremer.coser.control.ValidationError; -import fr.ifremer.coser.data.AbstractDataEntity; -import fr.ifremer.coser.data.Catch; -import fr.ifremer.coser.data.Haul; -import fr.ifremer.coser.data.Length; -import fr.ifremer.coser.data.Strata; -import fr.ifremer.coser.storage.DataStorage; - -/** - * Validation service. - * - * @author chatellier - * @version $Revision$ - * - * Last update : $Date$ - * By : $Author$ - */ -public class ValidationService { - - private static final Log log = LogFactory.getLog(ValidationService.class); - - protected CoserBusinessConfig config; - - protected ValidationAwareSupport validationSupport; - - protected DelegatingValidatorContext validationContext; - - protected ActionValidatorManager validator; - - protected ActionContext context; - - /** - * Initialise le context xworks. - * - * @param config configuration - */ - public ValidationService(CoserBusinessConfig config) { - this.config = config; - validationSupport = new ValidationAwareSupport(); - validationContext = new DelegatingValidatorContext(validationSupport); - - ConfigurationManager confManager = new ConfigurationManager(); - Configuration conf = confManager.getConfiguration(); - Container container = conf.getContainer(); - ValueStackFactory stackFactory = container.getInstance(ValueStackFactory.class); - ValueStack vs = stackFactory.createValueStack(); - - context = new ActionContext(vs.getContext()); - ActionContext.setContext(context); - - // huge improve cache performance - FileManager.setReloadingConfigs(false); - - // init validator - Container container2 = context.getContainer(); - validator = container2.getInstance(ActionValidatorManager.class, "no-annotations"); - } - - /** - * Valide un seul bean, retourne la liste des erreurs trouvées. - * - * @param bean bean to validate - * @param category result errors category - * @return - */ - protected List<ValidationError> validate(AbstractDataEntity bean, Category category) { - List<ValidationError> result = new ArrayList<ValidationError>();; - try { - // obligatoire pour les appel dans les thread et swing EDT - ActionContext.setContext(context); - - for(ValidationLevel validationLevel : ValidationLevel.values()) { - - try { - validator.validate(bean, validationLevel.getXWorkContext(), validationContext); - - if (validationContext.hasErrors()) { - Map<String, List<String>> fieldErrors = validationContext.getFieldErrors(); - - for (List<String> errors : fieldErrors.values()) { - ValidationError error = new ValidationError(); - error.setCategory(category); - String messages = StringUtils.join(errors, ", "); - error.setMessage(messages); - error.setLevel(validationLevel); - - // add error line - error.addLineNumber(bean.getLine()); - - result.add(error); - } - - Collection<String> actionMessages = validationContext.getActionErrors(); - if (CollectionUtils.isNotEmpty(actionMessages)) { - ValidationError error = new ValidationError(); - error.setCategory(category); - String messages = StringUtils.join(actionMessages, ", "); - error.setMessage(messages); - error.setLevel(validationLevel); - - // add error line - error.addLineNumber(bean.getLine()); - - result.add(error); - } - } - } - finally { - // vidage par log level - validationSupport.clearFieldErrors(); - validationSupport.clearActionErrors(); - } - } - - } catch (ValidationException ex) { - if (log.isErrorEnabled()) { - log.error("Can't validate bean", ex); - } - } - finally { - validationSupport.clearFieldErrors(); - validationSupport.clearActionErrors(); - } - return result; - } - - /** - * Valide toutes les données du projet. - * - * @param project project - * @param control control a valider - * @param progress progress monitor - * @return les erreurs de validation - */ - public List<ValidationError> validateData(Project project, Control control, ProgressMonitor progress) { - - // valide chaque category - List<ValidationError> validationErrors = new ArrayList<ValidationError>(); - for (Category category : Category.values()) { - if (category.isDataCategory()) { - // validation de la category seule (generique) - List<ValidationError> categoryErrors = validateCategoryXWork(control, category, progress); - if (categoryErrors != null) { - validationErrors.addAll(categoryErrors); - } - // validation specifique de la category - List<ValidationError> specificErrors = validateCategorySpecific(control, category, progress); - if (specificErrors != null) { - validationErrors.addAll(specificErrors); - } - } - } - - // validation entre catch et length (specific) - List<ValidationError> diffCatchLengthErrors = validateDiffCatchLength(control); - if (diffCatchLengthErrors != null) { - validationErrors.addAll(diffCatchLengthErrors); - } - - // validation par croisement de fichiers - List<ValidationError> crossFileErrors = validationCrossFiles(project, control, progress); - if (crossFileErrors != null) { - validationErrors.addAll(crossFileErrors); - } - - // cas particulier, s'il n'y a aucune erreur, on ajout une erreur de - // type info pour dire qu'il n'y a pas d'erreur - if (validationErrors.isEmpty()) { - ValidationError noErrorError = new ValidationError(); - noErrorError.setLevel(ValidationLevel.INFO); - noErrorError.setMessage(_("coser.business.control.noerrorfound")); - validationErrors.add(noErrorError); - } - return validationErrors; - } - - /** - * Valide une category entière d'un project. - * - * @param control control a valider - * @param category category a valider - * @param progress progress monitor - * @return les erreurs de validation - */ - public List<ValidationError> validateCategory(Control control, Category category, ProgressMonitor progress) { - - // validation de la category seule (generique) - List<ValidationError> errors = validateCategoryXWork(control, category, progress); - // validation specifique de la category - List<ValidationError> specificErrors = validateCategorySpecific(control, category, progress); - if (specificErrors != null) { - errors.addAll(specificErrors); - } - - return errors; - } - - /** - * Valide une category entière d'un project. - * - * @param control control a valider - * @param category category a valider - * @param progress progress monitor - * @return les erreurs de validation (not null) - */ - public List<ValidationError> validateCategoryXWork(Control control, Category category, ProgressMonitor progress) { - - // instance des bean utilisé lors de la validation - Catch beanCatch = new Catch(); - Haul beanHaul = new Haul(); - Length beanLength = new Length(); - Strata beanStrata = new Strata(); - - // get content depending on category - DataStorage dataToCheck = null; - switch (category) { - case CATCH: - dataToCheck = control.getCatch(); - break; - case HAUL: - dataToCheck = control.getHaul(); - break; - case LENGTH: - dataToCheck = control.getLength(); - break; - case STRATA: - dataToCheck = control.getStrata(); - break; - } - - // update progress - int total = dataToCheck.size() - 1; - if (progress != null) { - progress.setText(_("coser.business.control.step.xworks", _(category.getTranslationKey()), 0)); - progress.setTotal(total); - } - - Map<String, String> uniqueDataKeys = new HashMap<String, String>(); - List<ValidationError> validationErrors = new ArrayList<ValidationError>(); - Iterator<String[]> itDataToCheck = dataToCheck.iterator(); - int lineIndex = 1; // 1 = skip header - itDataToCheck.next(); // skip header - while (itDataToCheck.hasNext()) { - - // update progress - if (progress != null) { - int progressPercent = (int)((double)lineIndex / (double)total * 100.0); - progress.setText(_("coser.business.control.step.xworks", progressPercent)); - progress.setCurrent(lineIndex); - ++lineIndex; - } - - // first, do single bean validation - String[] line = itDataToCheck.next(); - List<ValidationError> errors = null; - switch (category) { - case CATCH: - beanCatch.setData(line); - errors = validate(beanCatch, category); - break; - case HAUL: - beanHaul.setData(line); - errors = validate(beanHaul, category); - break; - case LENGTH: - beanLength.setData(line); - errors = validate(beanLength, category); - break; - case STRATA: - beanStrata.setData(line); - errors = validate(beanStrata, category); - break; - } - if (errors != null) { - validationErrors.addAll(errors); - } - - // check for duplicated lines - String lineNumber = line[AbstractDataEntity.INDEX_LINE]; - String uniqueDataKey = getSignificantData(category, line); - if (uniqueDataKeys.containsKey(uniqueDataKey)) { - ValidationError error = new ValidationError(); - error.setCategory(category); - error.setLevel(ValidationLevel.ERROR); - error.addLineNumber(uniqueDataKeys.get(uniqueDataKey)); - error.addLineNumber(lineNumber); - error.setMessage(_("coser.business.control.error.duplicatedLine", uniqueDataKey)); - error.setDetailMessage(_("coser.business.control.error.duplicatedLineDetails", uniqueDataKey)); - validationErrors.add(error); - } - else { - uniqueDataKeys.put(uniqueDataKey, lineNumber); - } - } - - return validationErrors; - } - - /** - * Retourne sous forme de clé la partie significative des données. - * Donc sans le numéro de ligne suivant la catégorie. - * - * Utilisé pour la detection des doublons. - * - * CAPTURES - * Vérifier l'unicité sur : "Campagne", "Annee", "Trait", "Espece" - * - * STRATES - * Vérifier l'unicité sur : "Campagne", "Strate" - * - * TAILLES - * Vérifier l'unicité sur : Campagne", "Annee", "Trait", "Espece", "Sexe", "Maturite", "Longueur" - * - * TRAITS - * Vérifier l'unicité sur : "Campagne", "Annee", "Trait", "Mois" - * - * @param category category - * @param data data - * @return string key - */ - protected String getSignificantData(Category category, String[] data) { - StringBuilder sb = new StringBuilder(); - for (int index = 0 ; index < data.length ; ++index) { - // never count index line - if (index != AbstractDataEntity.INDEX_LINE) { - switch (category) { - case CATCH: - if (index == Catch.INDEX_SURVEY || index == Catch.INDEX_YEAR || index == Catch.INDEX_HAUL || index == Catch.INDEX_SPECIES) { - sb.append(data[index]).append('|'); - } - break; - case HAUL: - if (index == Haul.INDEX_SURVEY || index == Haul.INDEX_YEAR || index == Haul.INDEX_HAUL || index == Haul.INDEX_MONTH) { - sb.append(data[index]).append('|'); - } - break; - case LENGTH: - if (index == Length.INDEX_SURVEY || index == Length.INDEX_YEAR || index == Length.INDEX_HAUL || - index == Length.INDEX_SPECIES || index == Length.INDEX_SEX || index == Length.INDEX_MATURITY || index == Length.INDEX_LENGTH) { - sb.append(data[index]).append('|'); - } - break; - case STRATA: - if (index == Strata.INDEX_SURVEY || index == Strata.INDEX_STRATUM) { - sb.append(data[index]).append('|'); - } - break; - } - - } - - } - return sb.toString(); - } - - /** - * Effectue un calcul global, mais specific a chaque categorie. - * - * @param control control - * @param category category - * @param progress progress monitor - * @return - */ - protected List<ValidationError> validateCategorySpecific(Control control, - Category category, ProgressMonitor progress) { - - List<ValidationError> validationErrors = null; - switch (category) { - case CATCH: - validationErrors = validateCategorySpecificCatch(control, progress); - break; - case HAUL: - validationErrors = validateCategorySpecificHaul(control, progress); - break; - case LENGTH: - validationErrors = validateCategorySpecificLength(control, progress); - break; - case STRATA: - validationErrors = validateCategorySpecificStrata(control, progress); - break; - } - return validationErrors; - } - - /** - * Alerte si Somme(CAPTURES$Nombre par CAPTURES$Annee|Strate|Espece) < nobsmin. - * Erreur si CAPT$Nombre n'est pas 0 si le poids dans CAPT$Poids>0 - * - * @param control - * @param progress - * @return - */ - protected List<ValidationError> validateCategorySpecificCatch( - Control control, ProgressMonitor progress) { - - List<ValidationError> validationErrors = new ArrayList<ValidationError>(); - - int total = control.getCatch().size() - 1; - if (progress != null) { - progress.setText(_("coser.business.control.step.observation", 0)); - progress.setTotal(total); - } - - // "Campagne","Annee","Trait","Espece","Nombre","Poids" - // regroupement par campagne/annee/trait - Map<String, Double> nombreForKey = new HashMap<String, Double>(); - Map<String, String> firstLineForKey = new HashMap<String, String>(); - - // parcours des elements - Iterator<String[]> itTuple = control.getCatch().iterator(); - itTuple.next(); // skip header - int lineIndex = 1; // skip header - while (itTuple.hasNext()) { - String[] tuple = itTuple.next(); - - // update progress - if (progress != null) { - int progressPercent = (int)((double)lineIndex / (double)total * 100.0); - progress.setText(_("coser.business.control.step.observation", progressPercent)); - progress.setCurrent(lineIndex); - ++lineIndex; - } - - // compute key - StringBuffer sb = new StringBuffer(); - for (int tupleIndex = 0 ; tupleIndex < tuple.length; ++tupleIndex) { - if (tupleIndex == Catch.INDEX_YEAR || tupleIndex == Catch.INDEX_HAUL || - tupleIndex == Catch.INDEX_SPECIES) { - sb.append(tuple[tupleIndex]).append(';'); - } - } - - // store number sum - String nombreValue = tuple[Catch.INDEX_NUMBER]; - String lineNumber = tuple[AbstractDataEntity.INDEX_LINE]; - try { - Double nombre = Double.valueOf(nombreValue); - String key = sb.toString(); - if (nombreForKey.containsKey(key)) { - Double oldValue = nombreForKey.get(key); - Double newValue = oldValue + nombre; - nombreForKey.put(key, newValue); - } - else { - nombreForKey.put(key, nombre); - // sauvegarde la premiere ligne qui correspond a un regroupement de clé - firstLineForKey.put(key, lineNumber); - } - } - catch (NumberFormatException ex) { - // par trop grave, normalement les données deviennent - // valide au fil de la validation - if (log.isWarnEnabled()) { - log.warn("Can't parse '" + nombreValue + "' as double"); - } - } - } - - // now look for invalid data - for (Map.Entry<String, Double> sumObservation : nombreForKey.entrySet()) { - String key = sumObservation.getKey(); - Double value = sumObservation.getValue(); - if (value < config.getControlNobsmin()) { - - String lineNumber = firstLineForKey.get(key); - - ValidationError error = new ValidationError(); - error.setCategory(Category.CATCH); - error.setLevel(ValidationLevel.WARNING); - error.addLineNumber(lineNumber); - error.setMessage(_("coser.business.control.error.minObservationCount")); - error.setDetailMessage(_("coser.business.control.error.minObservationCountDetail", key, value)); - validationErrors.add(error); - } - } - - return validationErrors; - } - - /** - * Detecte des différences entre les nombres dans captures - * et les nombres dand taille. - * - * @see PublicationService#getCompareCatchLengthGraph(Project, Control, String) for details - * @see CoserBusinessConfig#getControlDiffCatchLength() for option - * - * @return errors - */ - protected List<ValidationError> validateDiffCatchLength(Control control) { - - List<ValidationError> validationErrors = new ArrayList<ValidationError>(); - SortedSet<String> setYear = new TreeSet<String>(); - - // look for data (data summed over catch) - Map<String, Map<String, Double>> catchForSpeciesYears = new HashMap<String, Map<String, Double>>(); - Iterator<String[]> itCatchData = control.getCatch().iterator(); - itCatchData.next(); // header - while (itCatchData.hasNext()) { - String[] tuple = itCatchData.next(); - String species = tuple[Catch.INDEX_SPECIES]; - - Map<String, Double> speciesCatchForYears = catchForSpeciesYears.get(species); - if (speciesCatchForYears == null) { - speciesCatchForYears = new HashMap<String, Double>(); - catchForSpeciesYears.put(species, speciesCatchForYears); - } - - String year = tuple[Catch.INDEX_YEAR]; - setYear.add(year); - String nombreValue = tuple[Catch.INDEX_NUMBER]; - try { - Double nombreDouble = Double.valueOf(nombreValue); - - if (speciesCatchForYears.containsKey(year)) { - Double oldValue = speciesCatchForYears.get(year); - Double newValue = oldValue + nombreDouble; - speciesCatchForYears.put(year, newValue); - } - else { - speciesCatchForYears.put(year, nombreDouble); - } - } - catch (NumberFormatException ex) { - if (log.isWarnEnabled()) { - log.warn("Can't parse " + nombreValue + " as double"); - } - } - } - - // look for data (data summed over length) - Map<String, Map<String, Double>> lengthForSpeciesYears = new HashMap<String, Map<String, Double>>(); - Iterator<String[]> itLengthData = control.getLength().iterator(); - itLengthData.next(); // header - while (itLengthData.hasNext()) { - String[] tuple = itLengthData.next(); - String species = tuple[Length.INDEX_SPECIES]; - - Map<String, Double> speciesLengthForYears = lengthForSpeciesYears.get(species); - if (speciesLengthForYears == null) { - speciesLengthForYears = new HashMap<String, Double>(); - lengthForSpeciesYears.put(species, speciesLengthForYears); - } - - String year = tuple[Length.INDEX_YEAR]; - setYear.add(year); - String nombreValue = tuple[Length.INDEX_NUMBER]; - try { - Double nombreDouble = Double.valueOf(nombreValue); - - if (speciesLengthForYears.containsKey(year)) { - Double oldValue = speciesLengthForYears.get(year); - Double newValue = oldValue + nombreDouble; - speciesLengthForYears.put(year, newValue); - } - else { - speciesLengthForYears.put(year, nombreDouble); - } - } - catch (NumberFormatException ex) { - if (log.isWarnEnabled()) { - log.warn("Can't parse " + nombreValue + " as double"); - } - } - } - - // check for all species and years - for (Map.Entry<String, Map<String, Double>> catchEntries : catchForSpeciesYears.entrySet()) { - String species = catchEntries.getKey(); - Map<String, Double> catchNumbers = catchEntries.getValue(); - Map<String, Double> lengthNumbers = lengthForSpeciesYears.get(species); - - if (lengthNumbers == null) { - // on suppose que cette erreur est détectée par un autre controle - continue; - } - - Iterator<String> itYears = setYear.iterator(); - while (itYears.hasNext()) { - String year = itYears.next(); - - Double catchNumber = catchNumbers.get(year); - Double lengthNumber = lengthNumbers.get(year); - if (catchNumber == null) { - catchNumber = 0.0; // marche pou NA - } - if (lengthNumber == null) { - lengthNumber = 0.0; // marche pour NA - } - - // diff entre 4 et 5 = (5-4) * 100 / 5 - double min = Math.min(catchNumber, lengthNumber); - // si c'est 0, ou absence de catures, ce n'est pas une erreur - if (min > 0) { - double max = Math.max(catchNumber, lengthNumber); - double diff = (max - min) * 100 / max; - - if (diff > config.getControlDiffCatchLength()) { - DiffCatchLengthValidationError error = new DiffCatchLengthValidationError(); - error.setSpecies(species); - error.setLevel(ValidationLevel.WARNING); - error.setMessage(_("coser.business.control.error.diffCatchLength")); - error.setDetailMessage(_("coser.business.control.error.diffCatchLengthDetail", species, year)); - validationErrors.add(error); - } - } - } - } - - return validationErrors; - } - - /** - * Alerte si Somme(TAILLES$Nombre par TAILLES$Annee|Strate|Espece) < nobsmin - * - * @param control - * @param progress - * @return - */ - protected List<ValidationError> validateCategorySpecificLength( - Control control, ProgressMonitor progress) { - List<ValidationError> validationErrors = new ArrayList<ValidationError>(); - - int total = control.getLength().size() - 1; - if (progress != null) { - progress.setText(_("coser.business.control.step.observation", 0)); - progress.setTotal(total); - } - - // "Campagne","Annee","Trait","Espece","Nombre","Poids" - // regroupement par campagne/annee/trait - Map<String, Double> nombreForKey = new HashMap<String, Double>(); - Map<String, String> firstLineForKey = new HashMap<String, String>(); - - // parcours des elements - Iterator<String[]> itTuple = control.getLength().iterator(); - itTuple.next(); // skip header - int lineIndex = 1; // skip header - while (itTuple.hasNext()) { - String[] tuple = itTuple.next(); - - // update progress - if (progress != null) { - int progressPercent = (int)((double)lineIndex / (double)total * 100.0); - progress.setText(_("coser.business.control.step.observation", progressPercent)); - progress.setCurrent(lineIndex); - ++lineIndex; - } - - // compute key - StringBuffer sb = new StringBuffer(); - for (int tupleIndex = 0 ; tupleIndex < tuple.length; ++tupleIndex) { - if (tupleIndex == Length.INDEX_YEAR || tupleIndex == Length.INDEX_HAUL || - tupleIndex == Length.INDEX_SPECIES) { - sb.append(tuple[tupleIndex]).append(';'); - } - } - - // store number sum - String nombreValue = tuple[Length.INDEX_NUMBER]; - String lineNumber = tuple[AbstractDataEntity.INDEX_LINE]; - try { - Double nombre = Double.valueOf(nombreValue); - String key = sb.toString(); - if (nombreForKey.containsKey(key)) { - Double oldValue = nombreForKey.get(key); - Double newValue = oldValue + nombre; - nombreForKey.put(key, newValue); - } - else { - nombreForKey.put(key, nombre); - // sauvegarde la premiere ligne qui correspond a un regroupement de clé - firstLineForKey.put(key, lineNumber); - } - } - catch (NumberFormatException ex) { - // par trop grave, normalement les données deviennet - // valide au fil de la validation - if (log.isWarnEnabled()) { - log.warn("Can't parse " + nombreValue + " as double"); - } - } - } - - // now look for invalid data - for (Map.Entry<String, Double> sumObservation : nombreForKey.entrySet()) { - String key = sumObservation.getKey(); - Double value = sumObservation.getValue(); - if (value < config.getControlNobsmin()) { - - String lineNumber = firstLineForKey.get(key); - - ValidationError error = new ValidationError(); - error.setCategory(Category.LENGTH); - error.setLevel(ValidationLevel.WARNING); - error.addLineNumber(lineNumber); - error.setMessage(_("coser.business.control.error.minObservationCount")); - error.setDetailMessage(_("coser.business.control.error.minObservationCountDetail", key, value)); - validationErrors.add(error); - } - } - - return validationErrors; - } - - /** - * @param control - * @param progress - * @return - */ - protected List<ValidationError> validateCategorySpecificHaul(Control control, - ProgressMonitor progress) { - return null; - } - - /** - * @param control - * @param progress - * @return - */ - protected List<ValidationError> validateCategorySpecificStrata( - Control control, ProgressMonitor progress) { - return null; - } - - /** - * Validation par croisement de fichiers. - * - * Les erreurs relevé ici porte sur plusieurs fichiers. - * - * @param control control - * @param progress progress - * @return error list - */ - protected List<ValidationError> validationCrossFiles(Project project, Control control, - ProgressMonitor progress) { - - if (progress != null) { - progress.setText(_("coser.business.control.step.crossFileChech", 0)); - progress.setTotal(100); - progress.setCurrent(0); - } - - List<ValidationError> crossFilesErrors = new ArrayList<ValidationError>(); - - // get all iterators - Iterator<String[]> itReftax = project.getRefTaxSpecies().iterator(); - itReftax.next(); // skip header - Iterator<String[]> itCatch = control.getCatch().iterator(); - itCatch.next(); // skip header - Iterator<String[]> itLength = control.getLength().iterator(); - itLength.next(); // skip header - Iterator<String[]> itStrata = control.getStrata().iterator(); - itStrata.next(); // skip header - Iterator<String[]> itHaul = control.getHaul().iterator(); - itHaul.next(); // skip header - - // declare all necessary data - Set<String> refTaxSpecies = new HashSet<String>(); - Set<String> catchYear = new HashSet<String>(); - Set<String> lengthYear = new HashSet<String>(); - Set<String> haulYear = new HashSet<String>(); - Set<String> campagneNames = new HashSet<String>(); - Set<String> lengthYearHaulSpecies = new HashSet<String>(); - Set<String> catchYearHaulSpecies = new HashSet<String>(); - Set<String> haulYearHaul = new HashSet<String>(); - Set<String> lengthYearHaul = new HashSet<String>(); - Set<String> catchYearHaul = new HashSet<String>(); - - // parcourt de toutes les données - while (itReftax.hasNext() ) { - String[] tuple = itReftax.next(); - // "C_Perm","NumSys","NivSys","C_VALIDE","L_VALIDE","AA_VALIDE","C_TxP\u00E8re","Taxa" - refTaxSpecies.add(tuple[3]); - } - - while (itCatch.hasNext()) { - String[] catchData = itCatch.next(); - campagneNames.add(catchData[Catch.INDEX_SURVEY]); - catchYear.add(catchData[Catch.INDEX_YEAR]); - catchYearHaulSpecies.add(catchData[Catch.INDEX_YEAR] + "|" + catchData[Catch.INDEX_HAUL] + "|" + catchData[Catch.INDEX_SPECIES]); - catchYearHaul.add(catchData[Catch.INDEX_YEAR] + "|" + catchData[Catch.INDEX_HAUL]); - - // Contrôle des noms d'espèces dans fichiers CAPTURES et TAILLES qui - // doivent être dans le référentiel "reftax": CAPTURES$Espece et - // TAILLES$Espece doivent exister dans REFTAX$ C_VALIDE - if (!refTaxSpecies.contains(catchData[Catch.INDEX_SPECIES])) { - ValidationError error = new ValidationError(); - error.setCategory(Category.CATCH); - error.addLineNumber(catchData[Catch.INDEX_LINE]); - error.setLevel(ValidationLevel.ERROR); - error.setMessage(_("coser.business.control.error.nonExistantSpecies")); - error.setDetailMessage(_("coser.business.control.error.nonExistantSpeciesDetail", catchData[Catch.INDEX_SPECIES])); - crossFilesErrors.add(error); - } - } - - if (progress != null) { - progress.setText(_("coser.business.control.step.crossFileChech", 25)); - progress.setCurrent(25); - } - - while (itLength.hasNext()) { - String[] lengthData = itLength.next(); - campagneNames.add(lengthData[Length.INDEX_SURVEY]); - lengthYear.add(lengthData[Length.INDEX_YEAR]); - lengthYearHaulSpecies.add(lengthData[Length.INDEX_YEAR] + "|" + - lengthData[Length.INDEX_HAUL] + "|" + lengthData[Length.INDEX_SPECIES]); - lengthYearHaul.add(lengthData[Length.INDEX_YEAR] + "|" + lengthData[Length.INDEX_HAUL]); - - // Contrôle des noms d'espèces dans fichiers CAPTURES et TAILLES qui - // doivent être dans le référentiel "reftax": CAPTURES$Espece et - // TAILLES$Espece doivent exister dans REFTAX$ C_VALIDE - if (!refTaxSpecies.contains(lengthData[Length.INDEX_SPECIES])) { - ValidationError error = new ValidationError(); - error.setCategory(Category.LENGTH); - error.addLineNumber(lengthData[Length.INDEX_LINE]); - error.setLevel(ValidationLevel.ERROR); - error.setMessage(_("coser.business.control.error.nonExistantSpecies")); - error.setDetailMessage(_("coser.business.control.error.nonExistantSpeciesDetail", lengthData[Length.INDEX_SPECIES])); - crossFilesErrors.add(error); - } - } - - if (progress != null) { - progress.setText(_("coser.business.control.step.crossFileChech", 50)); - progress.setCurrent(50); - } - - while (itStrata.hasNext()) { - String[] strataData = itStrata.next(); - campagneNames.add(strataData[Strata.INDEX_SURVEY]); - } - - if (progress != null) { - progress.setText(_("coser.business.control.step.crossFileChech", 75)); - progress.setCurrent(75); - } - - while (itHaul.hasNext()) { - String[] haulData = itHaul.next(); - campagneNames.add(haulData[Haul.INDEX_SURVEY]); - haulYear.add(haulData[Haul.INDEX_YEAR]); - haulYearHaul.add(haulData[Haul.INDEX_YEAR] + "|" + haulData[Haul.INDEX_HAUL]); - } - - // Vérifier que les mêmes années sont présentes dans fichiers captures - // traits et tailles: CAPTURES$Annee, TRAITS$Annee, TAILLES$Annee. - if (!catchYear.equals(lengthYear) || !haulYear.equals(lengthYear)) { - ValidationError error = new ValidationError(); - error.setLevel(ValidationLevel.FATAL); - error.setMessage(_("coser.business.control.error.yearsNotEquals")); - //error.setDetailMessage(_("coser.business.control.error.yearsNotEquals")); - crossFilesErrors.add(error); - } - - // Vérifier que le nom de la campagne est le même dans tous les fichiers *$Campagne. - if (campagneNames.size() != 1) { - ValidationError error = new ValidationError(); - error.setLevel(ValidationLevel.FATAL); - error.setMessage(_("coser.business.control.error.surveyNotEquals")); - //error.setDetailMessage(_("coser.business.control.error.surveyNotEquals")); - crossFilesErrors.add(error); - } - - // Vérifier que pour chaque espèce présent dans un trait d'une année - // dans le fichier tailles (noté Annee|Trait|Espece) il y a une donnée - // dans le fichier captures (le contraire n'est pas vraie) - Collection<String> missingSpeciesCatchTuples = CollectionUtils.subtract(lengthYearHaulSpecies, catchYearHaulSpecies); - for (String missingSpeciesCatchTuple : missingSpeciesCatchTuples) { - ValidationError error = new ValidationError(); - error.setLevel(ValidationLevel.FATAL); - error.setMessage(_("coser.business.control.error.missingYearHaulSpeciesForCatchData")); - error.setDetailMessage(_("coser.business.control.error.missingYearHaulSpeciesForCatchDataDetail", missingSpeciesCatchTuple)); - error.setTipMessage(_("coser.business.control.error.missingYearHaulSpeciesForCatchDataTip")); - crossFilesErrors.add(error); - } - - // Vérifier que pour chaque trait présent une année (noté Annee|Trait) - // dans les fichiers tailles et capt il y a une donnée dans le fichier - // traits - Collection<String> missingHaulLengthTuples = CollectionUtils.subtract(lengthYearHaul, haulYearHaul); - for (String missingHaulLengthTuple : missingHaulLengthTuples) { - ValidationError error = new ValidationError(); - error.setLevel(ValidationLevel.FATAL); - error.setMessage(_("coser.business.control.error.missingYearHaulForLengthData")); - error.setDetailMessage(_("coser.business.control.error.missingYearHaulForLengthDataDetail", missingHaulLengthTuple)); - error.setTipMessage(_("coser.business.control.error.missingYearHaulForLengthDataTip")); - crossFilesErrors.add(error); - } - - // Vérifier que pour chaque trait présent une année (noté Annee|Trait) - // dans les fichiers tailles et capt il y a une donnée dans le fichier - // traits - Collection<String> missingHaulCatchTuples = CollectionUtils.subtract(catchYearHaul, haulYearHaul); - for (String missingHaulCatchTuple : missingHaulCatchTuples) { - ValidationError error = new ValidationError(); - error.setLevel(ValidationLevel.FATAL); - error.setMessage(_("coser.business.control.error.missingYearHaulForCatchData")); - error.setDetailMessage(_("coser.business.control.error.missingYearHaulForCatchDataDetail", missingHaulCatchTuple)); - error.setTipMessage(_("coser.business.control.error.missingYearHaulForCatchDataTip")); - crossFilesErrors.add(error); - } - - if (progress != null) { - progress.setText(_("coser.business.control.step.crossFileChech", 100)); - progress.setCurrent(100); - } - - return crossFilesErrors; - } -} Modified: trunk/coser-business/src/test/java/fr/ifremer/coser/services/PublicationServiceTest.java =================================================================== --- trunk/coser-business/src/test/java/fr/ifremer/coser/services/PublicationServiceTest.java 2010-11-26 13:47:45 UTC (rev 291) +++ trunk/coser-business/src/test/java/fr/ifremer/coser/services/PublicationServiceTest.java 2010-11-26 14:03:54 UTC (rev 292) @@ -47,8 +47,8 @@ import fr.ifremer.coser.CoserConstants.Category; import fr.ifremer.coser.CoserConstants.ValidationLevel; import fr.ifremer.coser.bean.Project; -import fr.ifremer.coser.control.DiffCatchLengthValidationError; -import fr.ifremer.coser.control.ValidationError; +import fr.ifremer.coser.control.DiffCatchLengthControlError; +import fr.ifremer.coser.control.ControlError; /** * Publication service tests @@ -92,24 +92,24 @@ public void testExportHtml() throws CoserBusinessException, IOException { Project project = createTestProject(projectService, false); - List<ValidationError> validationErrors = new ArrayList<ValidationError>(); + List<ControlError> validationErrors = new ArrayList<ControlError>(); // error 1 : - ValidationError error1 = new ValidationError(); + ControlError error1 = new ControlError(); error1.setLevel(ValidationLevel.FATAL); error1.setMessage("Test fatal"); error1.addLineNumber("15"); error1.setCategory(Category.CATCH); validationErrors.add(error1); - DiffCatchLengthValidationError error2 = new DiffCatchLengthValidationError(); + DiffCatchLengthControlError error2 = new DiffCatchLengthControlError(); error2.setLevel(ValidationLevel.WARNING); error2.setMessage("Test warning and graph"); error2.setSpecies("COSER_SPECIES1"); error2.setTipMessage("Explication sur l'erreur"); validationErrors.add(error2); - ValidationError error3 = new ValidationError(); + ControlError error3 = new ControlError(); error3.setLevel(ValidationLevel.ERROR); error3.setMessage("Test error"); error3.addLineNumber("12"); Modified: trunk/coser-business/src/test/java/fr/ifremer/coser/services/ValidationServiceTest.java =================================================================== --- trunk/coser-business/src/test/java/fr/ifremer/coser/services/ValidationServiceTest.java 2010-11-26 13:47:45 UTC (rev 291) +++ trunk/coser-business/src/test/java/fr/ifremer/coser/services/ValidationServiceTest.java 2010-11-26 14:03:54 UTC (rev 292) @@ -34,7 +34,7 @@ import fr.ifremer.coser.CoserConstants.Category; import fr.ifremer.coser.bean.Control; -import fr.ifremer.coser.control.ValidationError; +import fr.ifremer.coser.control.ControlError; import fr.ifremer.coser.data.Catch; import fr.ifremer.coser.data.Haul; import fr.ifremer.coser.data.Length; @@ -54,7 +54,7 @@ private static final Log log = LogFactory.getLog(ValidationServiceTest.class); - protected ValidationService validationService = new ValidationService(config); + protected ControlService validationService = new ControlService(config); /** * Test les validations sur les champs vide. @@ -65,7 +65,7 @@ Catch myCatch = new Catch(); myCatch.setData(new String[]{"1", "","","","","",""}); - List<ValidationError> errors = validationService.validate(myCatch, Category.CATCH); + List<ControlError> errors = validationService.validate(myCatch, Category.CATCH); Assert.assertNotNull(errors); Assert.assertEquals(4, errors.size()); @@ -82,7 +82,7 @@ public void testDoubleValidation() { Catch myCatch = new Catch(); myCatch.setData(new String[]{"1", "Test survey","1999","Test trait","Test sp","NA","12"}); - List<ValidationError> errors = validationService.validate(myCatch, Category.CATCH); + List<ControlError> errors = validationService.validate(myCatch, Category.CATCH); log.warn(errors); Assert.assertTrue(errors.isEmpty()); } @@ -95,7 +95,7 @@ Length myLength = new Length(); // "Survey","Year","Haul","Species","Sex","Maturity","Length","Number","Weight","Age" myLength.setData(new String[]{"1", "Test survey","1999","Test trait","Test sp","i","m","23.25", "3.00", "44.99", "12"}); - List<ValidationError> errors = validationService.validate(myLength, Category.LENGTH); + List<ControlError> errors = validationService.validate(myLength, Category.LENGTH); log.warn(errors); Assert.assertEquals(1, errors.size()); } @@ -107,7 +107,7 @@ public void test5DecimalValidation() { Haul haulBean = new Haul(); haulBean.setData(new String[]{"1", "COSER_TEST","2010","TRAIT3","10","STR3","0.06","43.89","1.73","115.00"}); - List<ValidationError> errors = validationService.validate(haulBean, Category.HAUL); + List<ControlError> errors = validationService.validate(haulBean, Category.HAUL); Assert.assertEquals(3, errors.size()); haulBean.setData(new String[]{"1","COSER_TEST","2010","TRAIT3","10","STR3","0.06000","43.89000","1.73234","115.00"}); @@ -125,7 +125,7 @@ dataCatch.add(new String[]{"Line", "Campagne","Annee","Trait","Espece","Nombre","Poids"}); dataCatch.add(new String[]{"1", "Test survey","1999","Test trait","Test sp","0","12"}); control.setCatch(dataCatch); - List<ValidationError> errors = validationService.validateCategory(control, Category.CATCH, null); + List<ControlError> errors = validationService.validateCategory(control, Category.CATCH, null); if (log.isDebugEnabled()) { log.debug("Validation errors = " + errors); } Modified: trunk/coser-ui/src/main/java/fr/ifremer/coser/Coser.java =================================================================== --- trunk/coser-ui/src/main/java/fr/ifremer/coser/Coser.java 2010-11-26 13:47:45 UTC (rev 291) +++ trunk/coser-ui/src/main/java/fr/ifremer/coser/Coser.java 2010-11-26 14:03:54 UTC (rev 292) @@ -41,7 +41,7 @@ import fr.ifremer.coser.services.PublicationService; import fr.ifremer.coser.services.ImportService; import fr.ifremer.coser.services.ProjectService; -import fr.ifremer.coser.services.ValidationService; +import fr.ifremer.coser.services.ControlService; import fr.ifremer.coser.ui.CoserFrame; import fr.ifremer.coser.ui.util.ErrorHelper; @@ -138,7 +138,7 @@ context.setContextValue(session); context.setContextValue(new ProjectService(coserConfig)); context.setContextValue(new ImportService(coserConfig)); - context.setContextValue(new ValidationService(coserConfig)); + context.setContextValue(new ControlService(coserConfig)); context.setContextValue(new PublicationService(coserConfig)); // init frame with session reloading Copied: trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/control/ControlErrorTreeRenderer.java (from rev 284, trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/control/ControlValidationRenderer.java) =================================================================== --- trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/control/ControlErrorTreeRenderer.java (rev 0) +++ trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/control/ControlErrorTreeRenderer.java 2010-11-26 14:03:54 UTC (rev 292) @@ -0,0 +1,139 @@ +/* + * #%L + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2010 Codelutin, Chatellier Eric + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +package fr.ifremer.coser.ui.control; + +import static org.nuiton.i18n.I18n._; + +import java.awt.Component; + +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.JTree; +import javax.swing.tree.DefaultTreeCellRenderer; + +import jaxx.runtime.validator.swing.SwingValidatorUtil; +import fr.ifremer.coser.CoserConstants.Category; +import fr.ifremer.coser.CoserConstants.ValidationLevel; +import fr.ifremer.coser.control.ControlErrorGroup; +import fr.ifremer.coser.control.ControlError; + +/** + * Renderer pour le table des erreurs globales. + * + * @author chatellier + * @version $Revision$ + * + * Last update : $Date$ + * By : $Author$ + */ +public class ControlErrorTreeRenderer extends DefaultTreeCellRenderer { + + /** serialVersionUID. */ + private static final long serialVersionUID = -6423364126451874968L; + + protected ImageIcon fatalIcon = null; + protected ImageIcon errorIcon = null; + protected ImageIcon warningIcon = null; + protected ImageIcon infoIcon = null; + + public ControlErrorTreeRenderer() { + // use icon in jaxx + fatalIcon = SwingValidatorUtil.getFatalIcon(); + errorIcon = SwingValidatorUtil.getErrorIcon(); + warningIcon = SwingValidatorUtil.getWarningIcon(); + infoIcon = SwingValidatorUtil.getInfoIcon(); + } + + @Override + public Component getTreeCellRendererComponent(JTree tree, Object value, + boolean sel, boolean expanded, boolean leaf, int row, + boolean hasFocus) { + + GlobalControlErrorModel model = (GlobalControlErrorModel)tree.getModel(); + JLabel component = (JLabel)super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); + + ImageIcon icon = null; + String text = null; + String tooltipText = null; + + if (value instanceof String) { + text = _((String)value) + " (" + model.getChildCount(value) + ")"; + } + else if (value instanceof Category) { + text = _(((Category)value).getTranslationKey()) + " (" + model.getChildCount(value) + ")"; + } + else if (value instanceof ControlErrorGroup) { + ControlErrorGroup validationErrorGroup = (ControlErrorGroup)value; + ValidationLevel level = validationErrorGroup.getValidationLevel(); + switch (level) { + case FATAL: + icon = fatalIcon; + break; + case ERROR: + icon = errorIcon; + break; + case WARNING: + icon = warningIcon; + break; + case INFO: + icon = infoIcon; + break; + } + + String message = validationErrorGroup.getMessage(); + text = _(message) + " (" + model.getChildCount(value) + ")"; + } + else if (value instanceof ControlError) { + ControlError validationError = (ControlError)value; + ValidationLevel level = validationError.getLevel(); + switch (level) { + case FATAL: + icon = fatalIcon; + break; + case ERROR: + icon = errorIcon; + break; + case WARNING: + icon = warningIcon; + break; + case INFO: + icon = infoIcon; + break; + } + + String message = validationError.getDetailMessage(); + if (message == null) { + message = validationError.getMessage(); + } + text = _(message); + tooltipText = validationError.getTipMessage(); + } + + component.setText(text); + component.setIcon(icon); + component.setToolTipText(tooltipText); + + return component; + } +} Modified: trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/control/ControlHandler.java =================================================================== --- trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/control/ControlHandler.java 2010-11-26 13:47:45 UTC (rev 291) +++ trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/control/ControlHandler.java 2010-11-26 14:03:54 UTC (rev 292) @@ -76,8 +76,8 @@ import fr.ifremer.coser.CoserConstants.ValidationLevel; import fr.ifremer.coser.CoserException; import fr.ifremer.coser.bean.Project; -import fr.ifremer.coser.control.GlobalValidationGroup; -import fr.ifremer.coser.control.ValidationError; +import fr.ifremer.coser.control.ControlErrorGroup; +import fr.ifremer.coser.control.ControlError; import fr.ifremer.coser.data.AbstractDataEntity; import fr.ifremer.coser.data.Catch; import fr.ifremer.coser.data.Haul; @@ -85,7 +85,7 @@ import fr.ifremer.coser.data.Strata; import fr.ifremer.coser.services.ProjectService; import fr.ifremer.coser.services.PublicationService; -import fr.ifremer.coser.services.ValidationService; +import fr.ifremer.coser.services.ControlService; /** * Control handler for control view. @@ -317,19 +317,19 @@ * @param view */ public void checkData(final ControlView view) { - final ValidationService validationService = view.getContextValue(ValidationService.class); + final ControlService validationService = view.getContextValue(ControlService.class); final Project project = view.getContextValue(Project.class); //final Category category = (Category)view.getCategoryComboBox().getSelectedItem(); final ControlProgressBar progressBar = view.getCheckProgressBar(); - SwingWorker<List<ValidationError>, Void> task = new SwingWorker<List<ValidationError>, Void>() { + SwingWorker<List<ControlError>, Void> task = new SwingWorker<List<ControlError>, Void>() { long before = System.currentTimeMillis(); @Override - public List<ValidationError> doInBackground() { + public List<ControlError> doInBackground() { - List<ValidationError> validationErrors = validationService.validateData(project, project.getControl(), progressBar); + List<ControlError> validationErrors = validationService.validateData(project, project.getControl(), progressBar); return validationErrors; } @@ -341,20 +341,20 @@ } try { - List<ValidationError> errors = get(); - view.getGlobalValidationModel().setValidationErrors(errors); + List<ControlError> errors = get(); + view.getGlobalControlErrorModel().setControlErrors(errors); // hack parce que impossible de faire un fire // sans que les colonnes soit redimentionnées - SwingUtil.fixTableColumnWidth(view.getValidationGlobalErrorsTable(), 1, 25); + SwingUtil.fixTableColumnWidth(view.getGlobalControlErrorTable(), 1, 25); // active le bouton de sauvegarde si la liste d'erreur // ne contient pas de message d'erreur // warning, c'est ok boolean errorFound = false; - Iterator<ValidationError> itError = errors.iterator(); + Iterator<ControlError> itError = errors.iterator(); while (itError.hasNext() && !errorFound) { - ValidationError error = itError.next(); + ControlError error = itError.next(); if (error.getLevel() == ValidationLevel.ERROR || error.getLevel() == ValidationLevel.FATAL) { errorFound = true; } @@ -434,7 +434,7 @@ * @param event selection event */ public void showSelectedError(ControlView view, TreeSelectionEvent event) { - TreePath selectedError = view.getValidationGlobalErrorsTable().getTreeSelectionModel().getSelectionPath(); + TreePath selectedError = view.getGlobalControlErrorTable().getTreeSelectionModel().getSelectionPath(); if (selectedError != null) { Object[] pathWay = selectedError.getPath(); @@ -446,7 +446,7 @@ // swap category view.getCategoryComboBoxModel().setSelectedItem(category); - ValidationError error = (ValidationError)pathWay[3]; + ControlError error = (ControlError)pathWay[3]; Set<String> errorLineNumbers = error.getLineNumbers(); // peut être vide, si l'erreur ne porte pas sur un bean en particulier if (errorLineNumbers != null) { @@ -481,7 +481,7 @@ // clic contextuel if (event.getButton() == MouseEvent.BUTTON3) { - TreePath selectedError = controlView.getValidationGlobalErrorsTable().getTreeSelectionModel().getSelectionPath(); + TreePath selectedError = controlView.getGlobalControlErrorTable().getTreeSelectionModel().getSelectionPath(); JPopupMenu popupMenu = new JPopupMenu(_("coser.ui.control.globalErrorMenuLabel")); @@ -493,7 +493,7 @@ if (pathWay.length == 3) { if (pathWay[1] instanceof Category) { final Category category = (Category)pathWay[1]; - final GlobalValidationGroup validationGroup = (GlobalValidationGroup)pathWay[2]; + final ControlErrorGroup validationGroup = (ControlErrorGroup)pathWay[2]; JMenuItem replaceMenu = new JMenuItem(_("coser.ui.control.globalErrorMenuSelectAll")); replaceMenu.addActionListener(new ActionListener() { @@ -517,7 +517,7 @@ }); popupMenu.add(generateHtmlMenu); - popupMenu.show(controlView.getValidationGlobalErrorsTable(), event.getX(), event.getY()); + popupMenu.show(controlView.getGlobalControlErrorTable(), event.getX(), event.getY()); } } @@ -527,17 +527,17 @@ * @param controlView controlView * @param validationGroup validationGroup */ - protected void selectAllErrorGroupLines(ControlView controlView, Category category, GlobalValidationGroup validationGroup) { + protected void selectAllErrorGroupLines(ControlView controlView, Category category, ControlErrorGroup validationGroup) { // swap category controlView.getCategoryComboBoxModel().setSelectedItem(category); // select all lines - GlobalValidationModel model = controlView.getGlobalValidationModel(); + GlobalControlErrorModel model = controlView.getGlobalControlErrorModel(); controlView.getControlDataTableSelectionModel().clearSelection(); boolean first = true; int childCount = model.getChildCount(validationGroup); for (int indexChild = 0 ; indexChild < childCount ; ++indexChild) { - ValidationError validationError = (ValidationError)model.getChild(validationGroup, indexChild); + ControlError validationError = (ControlError)model.getChild(validationGroup, indexChild); Set<String> errorLineNumbers = validationError.getLineNumbers(); for (String errorLineNumber : errorLineNumbers) { @@ -591,10 +591,10 @@ Project project = controlView.getContextValue(Project.class); // get error list from table model - List<ValidationError> validationErrors = controlView.getGlobalValidationModel().getValidationErrors(); + List<ControlError> controlErrors = controlView.getGlobalControlErrorModel().getControlErrors(); File htmlFile = null; try { - htmlFile = publicationService.exportErrorsAsHTML(project, project.getControl(), validationErrors); + htmlFile = publicationService.exportErrorsAsHTML(project, project.getControl(), controlErrors); Desktop.getDesktop().browse(htmlFile.toURI()); } catch (CoserBusinessException ex) { Deleted: trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/control/ControlValidationRenderer.java =================================================================== --- trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/control/ControlValidationRenderer.java 2010-11-26 13:47:45 UTC (rev 291) +++ trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/control/ControlValidationRenderer.java 2010-11-26 14:03:54 UTC (rev 292) @@ -1,139 +0,0 @@ -/* - * #%L - * $Id$ - * $HeadURL$ - * %% - * Copyright (C) 2010 Codelutin, Chatellier Eric - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU 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 Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * <http://www.gnu.org/licenses/gpl-3.0.html>. - * #L% - */ - -package fr.ifremer.coser.ui.control; - -import static org.nuiton.i18n.I18n._; - -import java.awt.Component; - -import javax.swing.ImageIcon; -import javax.swing.JLabel; -import javax.swing.JTree; -import javax.swing.tree.DefaultTreeCellRenderer; - -import jaxx.runtime.validator.swing.SwingValidatorUtil; -import fr.ifremer.coser.CoserConstants.Category; -import fr.ifremer.coser.CoserConstants.ValidationLevel; -import fr.ifremer.coser.control.GlobalValidationGroup; -import fr.ifremer.coser.control.ValidationError; - -/** - * Renderer pour le table des erreurs globales. - * - * @author chatellier - * @version $Revision$ - * - * Last update : $Date$ - * By : $Author$ - */ -public class ControlValidationRenderer extends DefaultTreeCellRenderer { - - /** serialVersionUID. */ - private static final long serialVersionUID = -6423364126451874968L; - - protected ImageIcon fatalIcon = null; - protected ImageIcon errorIcon = null; - protected ImageIcon warningIcon = null; - protected ImageIcon infoIcon = null; - - public ControlValidationRenderer() { - // use icon in jaxx - fatalIcon = SwingValidatorUtil.getFatalIcon(); - errorIcon = SwingValidatorUtil.getErrorIcon(); - warningIcon = SwingValidatorUtil.getWarningIcon(); - infoIcon = SwingValidatorUtil.getInfoIcon(); - } - - @Override - public Component getTreeCellRendererComponent(JTree tree, Object value, - boolean sel, boolean expanded, boolean leaf, int row, - boolean hasFocus) { - - GlobalValidationModel model = (GlobalValidationModel)tree.getModel(); - JLabel component = (JLabel)super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); - - ImageIcon icon = null; - String text = null; - String tooltipText = null; - - if (value instanceof String) { - text = _((String)value) + " (" + model.getChildCount(value) + ")"; - } - else if (value instanceof Category) { - text = _(((Category)value).getTranslationKey()) + " (" + model.getChildCount(value) + ")"; - } - else if (value instanceof GlobalValidationGroup) { - GlobalValidationGroup validationErrorGroup = (GlobalValidationGroup)value; - ValidationLevel level = validationErrorGroup.getValidationLevel(); - switch (level) { - case FATAL: - icon = fatalIcon; - break; - case ERROR: - icon = errorIcon; - break; - case WARNING: - icon = warningIcon; - break; - case INFO: - icon = infoIcon; - break; - } - - String message = validationErrorGroup.getMessage(); - text = _(message) + " (" + model.getChildCount(value) + ")"; - } - else if (value instanceof ValidationError) { - ValidationError validationError = (ValidationError)value; - ValidationLevel level = validationError.getLevel(); - switch (level) { - case FATAL: - icon = fatalIcon; - break; - case ERROR: - icon = errorIcon; - break; - case WARNING: - icon = warningIcon; - break; - case INFO: - icon = infoIcon; - break; - } - - String message = validationError.getDetailMessage(); - if (message == null) { - message = validationError.getMessage(); - } - text = _(message); - tooltipText = validationError.getTipMessage(); - } - - component.setText(text); - component.setIcon(icon); - component.setToolTipText(tooltipText); - - return component; - } -} Modified: trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/control/ControlView.jaxx =================================================================== --- trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/control/ControlView.jaxx 2010-11-26 13:47:45 UTC (rev 291) +++ trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/control/ControlView.jaxx 2010-11-26 14:03:54 UTC (rev 292) @@ -30,7 +30,7 @@ new jaxx.runtime.validator.swing.SwingValidatorMessageTableRenderer()); // global validation - SwingUtil.fixTableColumnWidth(validationGlobalErrorsTable, 1, 25); + SwingUtil.fixTableColumnWidth(globalControlErrorTable, 1, 25); } ]]></script> @@ -130,14 +130,14 @@ <JSplitPane name="splitpaneGlobalErrorComment"> <JScrollPane> - <GlobalValidationModel id="globalValidationModel" /> - <JXTreeTable id='validationGlobalErrorsTable' treeTableModel="{globalValidationModel}" + <GlobalControlErrorModel id="globalControlErrorModel" /> + <JXTreeTable id='globalControlErrorTable' treeTableModel="{globalControlErrorModel}" rootVisible="false" showsRootHandles="true" - treeCellRenderer="{new ControlValidationRenderer()}" + treeCellRenderer="{new ControlErrorTreeRenderer()}" onMouseClicked="getHandler().showGlobalErrorTableContextMenu(this, event)" selectionMode="{ListSelectionModel.SINGLE_SELECTION}" /> - <javax.swing.tree.TreeSelectionModel id="globalValidationTableSelectionModel" - javaBean="validationGlobalErrorsTable.getTreeSelectionModel()" + <javax.swing.tree.TreeSelectionModel id="globalControlErrorSelectionModel" + javaBean="globalControlErrorTable.getTreeSelectionModel()" onValueChanged="getHandler().showSelectedError(this, event)" /> </JScrollPane> Copied: trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/control/GlobalControlErrorModel.java (from rev 284, trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/control/GlobalValidationModel.java) =================================================================== --- trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/control/GlobalControlErrorModel.java (rev 0) +++ trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/control/GlobalControlErrorModel.java 2010-11-26 14:03:54 UTC (rev 292) @@ -0,0 +1,291 @@ +/* + * #%L + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2010 Ifremer, Codelutin, Chatellier Eric + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +package fr.ifremer.coser.ui.control; + +import static org.nuiton.i18n.I18n._; +import static org.nuiton.i18n.I18n.n_; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.jdesktop.swingx.treetable.AbstractTreeTableModel; + +import fr.ifremer.coser.CoserConstants.Category; +import fr.ifremer.coser.CoserConstants.ValidationLevel; +import fr.ifremer.coser.control.ControlErrorGroup; +import fr.ifremer.coser.control.ControlError; + +/** + * Global validation table model. + * + * @author chatellier + * @version $Revision$ + * + * Last update : $Date$ + * By : $Author$ + */ +public class GlobalControlErrorModel extends AbstractTreeTableModel { + + /** serialVersionUID. */ + private static final long serialVersionUID = -32286733264427664L; + + protected List<ControlError> controlErrors; + protected List<Object> controlErrorCategory; + protected Map<Object, List<ControlErrorGroup>> controlCategoryChild; + protected Map<ControlErrorGroup, List<ControlError>> controlErrorsChilds; + protected Set<Object> checkedControlErrors; + + public GlobalControlErrorModel() { + super(1); + } + + public void setControlErrors(List<ControlError> controlErrors) { + this.controlErrors = controlErrors; + getValidationErrorAsMaps(controlErrors); + modelSupport.fireNewRoot(); + } + + public List<ControlError> getControlErrors() { + return controlErrors; + } + + /** + * Convert error list to tree structure. + * + * @param validationErrors list to convert + */ + protected void getValidationErrorAsMaps(List<ControlError> validationErrors) { + + controlCategoryChild = new HashMap<Object, List<ControlErrorGroup>>(); + controlErrorsChilds = new HashMap<ControlErrorGroup, List<ControlError>>(); + + for (ControlError validationError : validationErrors) { + + Object category = validationError.getCategory() == null ? + n_("coser.ui.control.error.allCategories") : validationError.getCategory(); + List<ControlErrorGroup> errorGroup = controlCategoryChild.get(category); + if (errorGroup == null) { + errorGroup = new ArrayList<ControlErrorGroup>(); + controlCategoryChild.put(category, errorGroup); + } + + ControlErrorGroup group = new ControlErrorGroup(validationError.getCategory(), validationError.getLevel(), validationError.getMessage()); + + List<ControlError> childErrors = controlErrorsChilds.get(group); + if (childErrors == null) { + childErrors = new ArrayList<ControlError>(); + controlErrorsChilds.put(group, childErrors); + errorGroup.add(group); + } + + childErrors.add(validationError); + } + + controlErrorCategory = new ArrayList<Object>(controlCategoryChild.keySet()); + Collections.sort(controlErrorCategory, new Comparator<Object>() { + @Override + public int compare(Object o1, Object o2) { + int result = -1; + if (o1 instanceof String) { + if (o2 instanceof String) { + result = ((String)o1).compareTo((String)o2); + } + } + else if (o1 instanceof Category) { + if (o2 instanceof Category) { + result = ((Category)o1).compareTo((Category)o2); + } + } + return result; + } + + }); + for (List<ControlErrorGroup> groups : controlCategoryChild.values()) { + Collections.sort(groups); + } + checkedControlErrors = new HashSet<Object>(); + } + + /* + * @see javax.swing.table.TableModel#getColumnCount() + */ + @Override + public int getColumnCount() { + return 2; + } + + + @Override + public String getColumnName(int columnIndex) { + + String result = null; + + switch (columnIndex) { + case 0: + result = _("coser.ui.control.global.message"); + break; + case 1: + result = _("coser.ui.control.global.done"); + break; + } + return result; + } + + @Override + public Class<?> getColumnClass(int columnIndex) { + Class<?> result = null; + + switch (columnIndex) { + case 0: + result = ValidationLevel.class; + break; + case 1: + result = Boolean.class; + break; + } + return result; + } + + /* + * @see org.jdesktop.swingx.treetable.TreeTableModel#getValueAt(java.lang.Object, int) + */ + @Override + public Object getValueAt(Object node, int column) { + + Object result = null; + + switch (column) { + case 0: + result = node; + break; + default: + result = checkedControlErrors.contains(node); + break; + } + + return result; + } + + /* + * @see javax.swing.tree.TreeModel#getChild(java.lang.Object, int) + */ + @Override + public Object getChild(Object parent, int index) { + + Object result = null; + + if (parent == getRoot()) { + result = controlErrorCategory.get(index); + } + else if (parent instanceof String || parent instanceof Category) { + result = controlCategoryChild.get(parent).get(index); + } + else if (parent instanceof ControlErrorGroup) { + List<ControlError> childError = controlErrorsChilds.get(parent); + result = childError.get(index); + } + + return result; + } + + /* + * @see javax.swing.tree.TreeModel#getChildCount(java.lang.Object) + */ + @Override + public int getChildCount(Object parent) { + int result = 0; + + if (parent == getRoot()) { + if (controlErrorCategory != null) { + result = controlErrorCategory.size(); + } + } + else if (parent instanceof String || parent instanceof Category) { + result = controlCategoryChild.get(parent).size(); + } + else if (parent instanceof ControlErrorGroup) { + List<ControlError> childError = controlErrorsChilds.get(parent); + result = childError.size(); + } + + return result; + } + + /* + * @see javax.swing.tree.TreeModel#getIndexOfChild(java.lang.Object, java.lang.Object) + */ + @Override + public int getIndexOfChild(Object parent, Object child) { + int result = -1; + + if (parent == getRoot()) { + result = controlErrorCategory.indexOf(child); + } + else if (parent instanceof String || parent instanceof Category) { + result = controlCategoryChild.get(parent).indexOf(child); + } + else if (parent instanceof ControlErrorGroup) { + List<ControlError> childError = controlErrorsChilds.get(parent); + result = childError.indexOf(child); + } + + return result; + } + + @Override + public boolean isCellEditable(Object node, int column) { + boolean result = false; + if (column == 1) { + result = true; + } + return result; + } + + @Override + public void setValueAt(Object value, Object node, int column) { + + Boolean booleanValue = (Boolean)value; + if (booleanValue.booleanValue()) { + checkedControlErrors.add(node); + } + else { + checkedControlErrors.remove(node); + } + + // recursive check of sub errors + if (node instanceof String || node instanceof Category || node instanceof ControlErrorGroup) { + int childCount = getChildCount(node); + for (int i = 0 ; i < childCount; ++i) { + Object child = getChild(node, i); + setValueAt(value, child, column); + } + } + } +} Deleted: trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/control/GlobalValidationModel.java =================================================================== --- trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/control/GlobalValidationModel.java 2010-11-26 13:47:45 UTC (rev 291) +++ trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/control/GlobalValidationModel.java 2010-11-26 14:03:54 UTC (rev 292) @@ -1,291 +0,0 @@ -/* - * #%L - * $Id$ - * $HeadURL$ - * %% - * Copyright (C) 2010 Ifremer, Codelutin, Chatellier Eric - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU 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 Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * <http://www.gnu.org/licenses/gpl-3.0.html>. - * #L% - */ - -package fr.ifremer.coser.ui.control; - -import static org.nuiton.i18n.I18n._; -import static org.nuiton.i18n.I18n.n_; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.jdesktop.swingx.treetable.AbstractTreeTableModel; - -import fr.ifremer.coser.CoserConstants.Category; -import fr.ifremer.coser.CoserConstants.ValidationLevel; -import fr.ifremer.coser.control.GlobalValidationGroup; -import fr.ifremer.coser.control.ValidationError; - -/** - * Global validation table model. - * - * @author chatellier - * @version $Revision$ - * - * Last update : $Date$ - * By : $Author$ - */ -public class GlobalValidationModel extends AbstractTreeTableModel { - - /** serialVersionUID. */ - private static final long serialVersionUID = -32286733264427664L; - - protected List<ValidationError> validationErrors; - protected List<Object> validationErrorCategory; - protected Map<Object, List<GlobalValidationGroup>> validationCategoryChild; - protected Map<GlobalValidationGroup, List<ValidationError>> validationErrorsChilds; - protected Set<Object> checkedValidationErrors; - - public GlobalValidationModel() { - super(1); - } - - public void setValidationErrors(List<ValidationError> validationErrors) { - this.validationErrors = validationErrors; - getValidationErrorAsMaps(validationErrors); - modelSupport.fireNewRoot(); - } - - public List<ValidationError> getValidationErrors() { - return validationErrors; - } - - /** - * Convert error list to tree structure. - * - * @param validationErrors list to convert - */ - protected void getValidationErrorAsMaps(List<ValidationError> validationErrors) { - - validationCategoryChild = new HashMap<Object, List<GlobalValidationGroup>>(); - validationErrorsChilds = new HashMap<GlobalValidationGroup, List<ValidationError>>(); - - for (ValidationError validationError : validationErrors) { - - Object category = validationError.getCategory() == null ? - n_("coser.ui.control.error.allCategories") : validationError.getCategory(); - List<GlobalValidationGroup> errorGroup = validationCategoryChild.get(category); - if (errorGroup == null) { - errorGroup = new ArrayList<GlobalValidationGroup>(); - validationCategoryChild.put(category, errorGroup); - } - - GlobalValidationGroup group = new GlobalValidationGroup(validationError.getCategory(), validationError.getLevel(), validationError.getMessage()); - - List<ValidationError> childErrors = validationErrorsChilds.get(group); - if (childErrors == null) { - childErrors = new ArrayList<ValidationError>(); - validationErrorsChilds.put(group, childErrors); - errorGroup.add(group); - } - - childErrors.add(validationError); - } - - validationErrorCategory = new ArrayList<Object>(validationCategoryChild.keySet()); - Collections.sort(validationErrorCategory, new Comparator<Object>() { - @Override - public int compare(Object o1, Object o2) { - int result = -1; - if (o1 instanceof String) { - if (o2 instanceof String) { - result = ((String)o1).compareTo((String)o2); - } - } - else if (o1 instanceof Category) { - if (o2 instanceof Category) { - result = ((Category)o1).compareTo((Category)o2); - } - } - return result; - } - - }); - for (List<GlobalValidationGroup> groups : validationCategoryChild.values()) { - Collections.sort(groups); - } - checkedValidationErrors = new HashSet<Object>(); - } - - /* - * @see javax.swing.table.TableModel#getColumnCount() - */ - @Override - public int getColumnCount() { - return 2; - } - - - @Override - public String getColumnName(int columnIndex) { - - String result = null; - - switch (columnIndex) { - case 0: - result = _("coser.ui.control.global.message"); - break; - case 1: - result = _("coser.ui.control.global.done"); - break; - } - return result; - } - - @Override - public Class<?> getColumnClass(int columnIndex) { - Class<?> result = null; - - switch (columnIndex) { - case 0: - result = ValidationLevel.class; - break; - case 1: - result = Boolean.class; - break; - } - return result; - } - - /* - * @see org.jdesktop.swingx.treetable.TreeTableModel#getValueAt(java.lang.Object, int) - */ - @Override - public Object getValueAt(Object node, int column) { - - Object result = null; - - switch (column) { - case 0: - result = node; - break; - default: - result = checkedValidationErrors.contains(node); - break; - } - - return result; - } - - /* - * @see javax.swing.tree.TreeModel#getChild(java.lang.Object, int) - */ - @Override - public Object getChild(Object parent, int index) { - - Object result = null; - - if (parent == getRoot()) { - result = validationErrorCategory.get(index); - } - else if (parent instanceof String || parent instanceof Category) { - result = validationCategoryChild.get(parent).get(index); - } - else if (parent instanceof GlobalValidationGroup) { - List<ValidationError> childError = validationErrorsChilds.get(parent); - result = childError.get(index); - } - - return result; - } - - /* - * @see javax.swing.tree.TreeModel#getChildCount(java.lang.Object) - */ - @Override - public int getChildCount(Object parent) { - int result = 0; - - if (parent == getRoot()) { - if (validationErrorCategory != null) { - result = validationErrorCategory.size(); - } - } - else if (parent instanceof String || parent instanceof Category) { - result = validationCategoryChild.get(parent).size(); - } - else if (parent instanceof GlobalValidationGroup) { - List<ValidationError> childError = validationErrorsChilds.get(parent); - result = childError.size(); - } - - return result; - } - - /* - * @see javax.swing.tree.TreeModel#getIndexOfChild(java.lang.Object, java.lang.Object) - */ - @Override - public int getIndexOfChild(Object parent, Object child) { - int result = -1; - - if (parent == getRoot()) { - result = validationErrorCategory.indexOf(child); - } - else if (parent instanceof String || parent instanceof Category) { - result = validationCategoryChild.get(parent).indexOf(child); - } - else if (parent instanceof GlobalValidationGroup) { - List<ValidationError> childError = validationErrorsChilds.get(parent); - result = childError.indexOf(child); - } - - return result; - } - - @Override - public boolean isCellEditable(Object node, int column) { - boolean result = false; - if (column == 1) { - result = true; - } - return result; - } - - @Override - public void setValueAt(Object value, Object node, int column) { - - Boolean booleanValue = (Boolean)value; - if (booleanValue.booleanValue()) { - checkedValidationErrors.add(node); - } - else { - checkedValidationErrors.remove(node); - } - - // recursive check of sub errors - if (node instanceof String || node instanceof Category || node instanceof GlobalValidationGroup) { - int childCount = getChildCount(node); - for (int i = 0 ; i < childCount; ++i) { - Object child = getChild(node, i); - setValueAt(value, child, column); - } - } - } -}
participants (1)
-
chatellier@users.labs.libre-entreprise.org