r103 - in trunk: coser-business/src/main/java/fr/ifremer/coser coser-business/src/main/java/fr/ifremer/coser/bean coser-business/src/main/java/fr/ifremer/coser/command coser-business/src/main/java/fr/ifremer/coser/services coser-business/src/main/java/fr/ifremer/coser/storage coser-business/src/test/java/fr/ifremer/coser/services coser-ui/src/main/java/fr/ifremer/coser/ui/control coser-ui/src/main/java/fr/ifremer/coser/ui/selection coser-ui/src/main/resources/i18n
Author: chatellier Date: 2010-10-26 13:59:58 +0000 (Tue, 26 Oct 2010) New Revision: 103 Log: Implementation du pattern command pour les operations sur les donn?\195?\169es. Added: trunk/coser-business/src/main/java/fr/ifremer/coser/command/ trunk/coser-business/src/main/java/fr/ifremer/coser/command/Command.java trunk/coser-business/src/main/java/fr/ifremer/coser/command/DeleteLineCommand.java trunk/coser-business/src/main/java/fr/ifremer/coser/command/MergeSpeciesCommand.java trunk/coser-business/src/main/java/fr/ifremer/coser/command/ModifyFieldCommand.java trunk/coser-business/src/main/java/fr/ifremer/coser/services/CommandService.java trunk/coser-business/src/test/java/fr/ifremer/coser/services/CommandServiceTest.java trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/control/ControlFindReplaceDialog.jaxx Modified: trunk/coser-business/src/main/java/fr/ifremer/coser/CoserConstants.java trunk/coser-business/src/main/java/fr/ifremer/coser/bean/Project.java trunk/coser-business/src/main/java/fr/ifremer/coser/services/ImportService.java trunk/coser-business/src/main/java/fr/ifremer/coser/services/ProjectService.java trunk/coser-business/src/main/java/fr/ifremer/coser/storage/DataStorage.java trunk/coser-business/src/main/java/fr/ifremer/coser/storage/MemoryListStorage.java trunk/coser-business/src/test/java/fr/ifremer/coser/services/CoserTestAbstract.java trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/control/ControlDataTableModel.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 trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/selection/SelectionListsView.jaxx trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/selection/SelectionResultView.jaxx trunk/coser-ui/src/main/resources/i18n/coser-ui-en_GB.properties trunk/coser-ui/src/main/resources/i18n/coser-ui-fr_FR.properties Modified: trunk/coser-business/src/main/java/fr/ifremer/coser/CoserConstants.java =================================================================== --- trunk/coser-business/src/main/java/fr/ifremer/coser/CoserConstants.java 2010-10-26 08:53:49 UTC (rev 102) +++ trunk/coser-business/src/main/java/fr/ifremer/coser/CoserConstants.java 2010-10-26 13:59:58 UTC (rev 103) @@ -58,12 +58,18 @@ /** Suffix des nom de fichiers data apres control. */ public static final String STORAGE_CONTROL_SUFFIX = "_co"; + + /** Suffix des nom de fichiers data contenant les données supprimées. */ + public static final String STORAGE_DELECTED_SUFFIX = "_del"; /** Suffix des nom de fichiers data apres selection. */ public static final String STORAGE_SELECTION_SUFFIX = "_se"; /** Extension des fichiers CSV. */ public static final String STORAGE_CSV_EXTENSION = ".csv"; + + /** Nom du fichier des historiques de modification */ + public static final String HISTORY_FILENAME = "history.csv"; /** Categories des données manipulées. */ public static enum Category { Modified: trunk/coser-business/src/main/java/fr/ifremer/coser/bean/Project.java =================================================================== --- trunk/coser-business/src/main/java/fr/ifremer/coser/bean/Project.java 2010-10-26 08:53:49 UTC (rev 102) +++ trunk/coser-business/src/main/java/fr/ifremer/coser/bean/Project.java 2010-10-26 13:59:58 UTC (rev 103) @@ -25,6 +25,9 @@ package fr.ifremer.coser.bean; +import java.util.List; + +import fr.ifremer.coser.command.Command; import fr.ifremer.coser.storage.DataStorage; /** @@ -54,15 +57,26 @@ protected DataStorage dataCatch; + protected DataStorage deletedDataCatch; + protected DataStorage dataStrata; + protected DataStorage deletedDataStrata; + protected DataStorage dataHaul; + protected DataStorage deletedDataHaul; + protected DataStorage dataLength; + + protected DataStorage deletedDataLength; /** Reftax SIH. */ protected DataStorage refTaxSpecies; + /** L'historique des commandes do/undo .*/ + protected List<Command> historyCommand; + public DataStorage getCatch() { return dataCatch; } @@ -105,6 +119,38 @@ this.dataLength = dataLength; } + public DataStorage getDeletedCatch() { + return deletedDataCatch; + } + + public void setDeletedCatch(DataStorage deletedDataCatch) { + this.deletedDataCatch = deletedDataCatch; + } + + public DataStorage getDeletedStrata() { + return deletedDataStrata; + } + + public void setDeletedStrata(DataStorage deletedDataStrata) { + this.deletedDataStrata = deletedDataStrata; + } + + public DataStorage getDeletedHaul() { + return deletedDataHaul; + } + + public void setDeletedHaul(DataStorage deletedDataHaul) { + this.deletedDataHaul = deletedDataHaul; + } + + public DataStorage getDeletedLength() { + return deletedDataLength; + } + + public void setDeletedLength(DataStorage deletedDataLength) { + this.deletedDataLength = deletedDataLength; + } + public DataStorage getRefTaxSpecies() { return refTaxSpecies; } @@ -112,4 +158,12 @@ public void setRefTaxSpecies(DataStorage refTaxSpecies) { this.refTaxSpecies = refTaxSpecies; } + + public List<Command> getHistoryCommand() { + return historyCommand; + } + + public void setHistoryCommand(List<Command> historyCommand) { + this.historyCommand = historyCommand; + } } Added: trunk/coser-business/src/main/java/fr/ifremer/coser/command/Command.java =================================================================== --- trunk/coser-business/src/main/java/fr/ifremer/coser/command/Command.java (rev 0) +++ trunk/coser-business/src/main/java/fr/ifremer/coser/command/Command.java 2010-10-26 13:59:58 UTC (rev 103) @@ -0,0 +1,87 @@ +/* + * #%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.command; + +import java.util.UUID; + +import fr.ifremer.coser.CoserBusinessException; +import fr.ifremer.coser.bean.Project; + +/** + * Command interface. + * + * @author chatellier + * @version $Revision$ + * + * Last update : $Date$ + * By : $Author$ + */ +public abstract class Command { + + /** + * UUID de la commande. Les commandes ayant un UUID commun font partie + * du même groupe de commandes. + */ + protected String commandUUID; + + /** + * UUID de la commande. Les commandes ayant un UUID commun font partie + * du même groupe de commandes. + * + * @return command UUID + * @see UUID + */ + public String getCommandUUID() { + return commandUUID; + } + + /** + * UUID de la commande. Les commandes ayant un UUID commun font partie + * du même groupe de commandes. + * + * @param commandUUID new uuid + * @see UUID + */ + public void setCommandUUID(String commandUUID) { + this.commandUUID = commandUUID; + } + + /** + * Do command on project. + * + * @param project project + * @throws CoserBusinessException + */ + public abstract void doCommand(Project project) throws CoserBusinessException; + + /** + * Undo command on project. + * + * @param project project + * @throws CoserBusinessException + */ + public abstract void undoCommand(Project project) throws CoserBusinessException; +} Property changes on: trunk/coser-business/src/main/java/fr/ifremer/coser/command/Command.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision HeadURL Added: trunk/coser-business/src/main/java/fr/ifremer/coser/command/DeleteLineCommand.java =================================================================== --- trunk/coser-business/src/main/java/fr/ifremer/coser/command/DeleteLineCommand.java (rev 0) +++ trunk/coser-business/src/main/java/fr/ifremer/coser/command/DeleteLineCommand.java 2010-10-26 13:59:58 UTC (rev 103) @@ -0,0 +1,197 @@ +/* + * #%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.command; + +import static org.nuiton.i18n.I18n._; + +import java.util.Iterator; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import fr.ifremer.coser.CoserBusinessException; +import fr.ifremer.coser.CoserConstants.Category; +import fr.ifremer.coser.bean.AbstractDataEntity; +import fr.ifremer.coser.bean.Project; +import fr.ifremer.coser.storage.DataStorage; + +/** + * Command pattern object. + * + * @author chatellier + * @version $Revision$ + * + * Last update : $Date$ + * By : $Author$ + */ +public class DeleteLineCommand extends Command { + + private static final Log log = LogFactory.getLog(DeleteLineCommand.class); + + /** Category de données sur lequel porte l'action. */ + protected Category category; + + /** Line index of object to do command to. */ + protected String lineIndex; + + public Category getCategory() { + return category; + } + + public void setCategory(Category category) { + this.category = category; + } + + public String getLineIndex() { + return lineIndex; + } + + public void setLineIndex(String lineIndex) { + this.lineIndex = lineIndex; + } + + @Override + public void doCommand(Project project) throws CoserBusinessException { + + DataStorage dataStorage = null; + DataStorage deletedDataStorage = null; + + // get data storage depending on category + switch(category) { + case CATCH: + dataStorage = project.getCatch(); + deletedDataStorage = project.getDeletedCatch(); + break; + case HAUL: + dataStorage = project.getHaul(); + deletedDataStorage = project.getDeletedHaul(); + break; + case LENGTH: + dataStorage = project.getLength(); + deletedDataStorage = project.getDeletedLength(); + break; + case STRATA: + dataStorage = project.getStrata(); + deletedDataStorage = project.getDeletedStrata(); + break; + } + + // parcourt des lignes + boolean dataLineFound = false; + Iterator<String[]> itDataStorage = dataStorage.iterator(); + itDataStorage.next(); // skip header + while (itDataStorage.hasNext()) { + String[] dataLine = itDataStorage.next(); + String dataLineIndex = dataLine[AbstractDataEntity.INDEX_LINE]; + if (dataLineIndex.equals(lineIndex)) { + if (log.isDebugEnabled()) { + log.debug("Removing line " + dataLineIndex); + } + dataLineFound = true; + deletedDataStorage.add(dataLine); + itDataStorage.remove(); + } + } + + // if not found, throw business exception + if (!dataLineFound) { + throw new CoserBusinessException(_("Can't find line %s for deletion", lineIndex)); + } + } + + @Override + public void undoCommand(Project project) throws CoserBusinessException { + DataStorage dataStorage = null; + DataStorage deletedDataStorage = null; + + // get data storage depending on category + switch(category) { + case CATCH: + dataStorage = project.getCatch(); + deletedDataStorage = project.getDeletedCatch(); + break; + case HAUL: + dataStorage = project.getHaul(); + deletedDataStorage = project.getDeletedHaul(); + break; + case LENGTH: + dataStorage = project.getLength(); + deletedDataStorage = project.getDeletedLength(); + break; + case STRATA: + dataStorage = project.getStrata(); + deletedDataStorage = project.getDeletedStrata(); + break; + } + + // parcourt des lignes + boolean dataLineFound = false; + Iterator<String[]> itDeletedDataStorage = deletedDataStorage.iterator(); + itDeletedDataStorage.next(); //skip header + while (itDeletedDataStorage.hasNext()) { + String[] deletedDataLine = itDeletedDataStorage.next(); + String deletedDataLineIndex = deletedDataLine[AbstractDataEntity.INDEX_LINE]; + if (deletedDataLineIndex.equals(lineIndex)) { + dataLineFound = true; + itDeletedDataStorage.remove(); + + // search new insert point + int originalDataLine = Integer.parseInt(deletedDataLineIndex); + Iterator<String[]> itDataStorage = dataStorage.iterator(); + itDataStorage.next(); //skip header + int index = 1; // due to header + while (itDataStorage.hasNext()) { + String[] dataLine = itDataStorage.next(); + String dataLineIndex = dataLine[AbstractDataEntity.INDEX_LINE]; + int dataLineInt = Integer.parseInt(dataLineIndex); + if (dataLineInt == originalDataLine) { + throw new CoserBusinessException(_("Original line already exists!")); + } + else if (dataLineInt > originalDataLine) { + break; + } + index++; + } + + // insert line at specified index + dataStorage.add(index, deletedDataLine); + if (log.isDebugEnabled()) { + log.debug("Restore line " + index); + } + } + } + + // if not found, throw business exception + if (!dataLineFound) { + throw new CoserBusinessException(_("Can't find line %s for undeletion", lineIndex)); + } + } + + @Override + public String toString() { + return "Delete line " + lineIndex + " on " + category; + } +} Property changes on: trunk/coser-business/src/main/java/fr/ifremer/coser/command/DeleteLineCommand.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision HeadURL Added: trunk/coser-business/src/main/java/fr/ifremer/coser/command/MergeSpeciesCommand.java =================================================================== --- trunk/coser-business/src/main/java/fr/ifremer/coser/command/MergeSpeciesCommand.java (rev 0) +++ trunk/coser-business/src/main/java/fr/ifremer/coser/command/MergeSpeciesCommand.java 2010-10-26 13:59:58 UTC (rev 103) @@ -0,0 +1,213 @@ +/* + * #%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 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.command; + +import java.util.Iterator; + +import org.apache.commons.lang.NotImplementedException; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import fr.ifremer.coser.CoserBusinessException; +import fr.ifremer.coser.bean.Catch; +import fr.ifremer.coser.bean.Project; + +/** + * Merge species command. + * + * @author chatellier + * @version $Revision$ + * + * Last update : $Date$ + * By : $Author$ + */ +public class MergeSpeciesCommand extends Command { + + private static final Log log = LogFactory.getLog(MergeSpeciesCommand.class); + + protected String newSpecyName; + + protected String[] speciesNames; + + public String getNewSpecyName() { + return newSpecyName; + } + + public void setNewSpecyName(String newSpecyName) { + this.newSpecyName = newSpecyName; + } + + public String[] getSpeciesNames() { + return speciesNames; + } + + public void setSpeciesNames(String[] speciesNames) { + this.speciesNames = speciesNames; + } + + /* + * @see fr.ifremer.coser.command.Command#doCommand(fr.ifremer.coser.bean.Project) + */ + @Override + public void doCommand(Project project) throws CoserBusinessException { + + project = mergeCatch(project, newSpecyName, speciesNames); + project = mergeLength(project, newSpecyName, speciesNames); + + } + + /* + * @see fr.ifremer.coser.command.Command#undoCommand(fr.ifremer.coser.bean.Project) + */ + @Override + public void undoCommand(Project project) throws CoserBusinessException { + throw new NotImplementedException("Merge operation can't be undone"); + + } + + /** + * Fusion d'espece dans les données de captures. + * + * @param project project + * @param newSpecyName new specy name (after merge) + * @param speciesNames species name to merge + * @return project + */ + protected Project mergeLength(Project project, String newSpecyName, + String... speciesNames) { + return project; + } + + /** + * Fusion d'especes dans les données de taille. + * + * @param project project + * @param newSpecyName new specy name (after merge) + * @param speciesNames species name to merge + * @return project + */ + protected Project mergeCatch(Project project, String newSpecyName, + String... speciesNames) { + + // "Campagne","Annee","Trait","Espece","Nombre","Poids" + + // regroupement par campagne/annee/trait + String lastCampagne = null; + String lastAnnee = null; + String lastTraits = null; + String[] tupleFound = null; + int tupleIndex = -1; + + // parcours des elements + Iterator<String[]> itTuple = project.getCatch().iterator(); + itTuple.next(); // skip header + int index = 1; // skip header + while (itTuple.hasNext()) { + String[] tuple = itTuple.next(); + + // si les valeurs servant au bornes de regroupement + // on changer, on reset les données du merge + String currentCampagne = tuple[Catch.INDEX_SURVEY]; + String currentAnnee = tuple[Catch.INDEX_YEAR]; + String currentTraits = tuple[Catch.INDEX_HAUL]; + if (!currentCampagne.equals(lastCampagne) || + !currentAnnee.equals(lastAnnee) || + !currentTraits.equals(lastTraits)) { + + tupleFound = null; + lastCampagne = currentCampagne; + lastAnnee = currentAnnee; + lastTraits = currentTraits; + } + + // test si l'espece en cours fait partie de celle a merger + String species = tuple[Catch.INDEX_SPECIES]; + boolean specyFound = false; + for (String specy : speciesNames) { + if (specy.equals(species)) { + specyFound = true; + } + } + + // si l'espece est a merger, on se souvient du tuple + // principale a merge, ou on merge avec le tuple + // principal si on a deja un tuple principal + if (specyFound) { + if (tupleFound == null) { + tupleFound = tuple; + tupleFound[3] = newSpecyName; + tupleIndex = index; + } + else { + tupleFound = mergeCatches(tupleFound, tuple); + // et on supprime le tuple + // qui a ete merge + itTuple.remove(); + // bidouille le remove decalle les index suivants + index--; + } + + project.getCatch().set(tupleIndex, tupleFound); + } + + index++; + } + + return project; + } + + /** + * Merge deux lines des catch. + * + * Le resultat est mergé dans {@code tuple1} et retourné. + * + * Somme les "Nombre" et "Poids" + * + * @param tuple1 tuple1 + * @param tuple2 tuple2 + * @return tuple1 + */ + protected String[] mergeCatches(String[] tuple1, String[] tuple2) { + + // "Campagne","Annee","Trait","Espece","Nombre","Poids" + + try { + double nombre1 = Double.parseDouble(tuple1[Catch.INDEX_NUMBER]); + double nombre2 = Double.parseDouble(tuple2[Catch.INDEX_NUMBER]); + double poids1 = Double.parseDouble(tuple1[Catch.INDEX_WEIGHT]); + double poids2 = Double.parseDouble(tuple2[Catch.INDEX_WEIGHT]); + + tuple1[Catch.INDEX_NUMBER] = String.valueOf(nombre1 + nombre2); + tuple1[Catch.INDEX_WEIGHT] = String.valueOf(poids1 + poids2); + } + catch (NumberFormatException ex) { + if (log.isWarnEnabled()) { + log.warn("Can't convert data as double for merge", ex); + } + } + return tuple1; + } +} Property changes on: trunk/coser-business/src/main/java/fr/ifremer/coser/command/MergeSpeciesCommand.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision HeadURL Added: trunk/coser-business/src/main/java/fr/ifremer/coser/command/ModifyFieldCommand.java =================================================================== --- trunk/coser-business/src/main/java/fr/ifremer/coser/command/ModifyFieldCommand.java (rev 0) +++ trunk/coser-business/src/main/java/fr/ifremer/coser/command/ModifyFieldCommand.java 2010-10-26 13:59:58 UTC (rev 103) @@ -0,0 +1,132 @@ +/* + * #%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.command; + +import fr.ifremer.coser.CoserBusinessException; +import fr.ifremer.coser.CoserConstants.Category; +import fr.ifremer.coser.bean.Catch; +import fr.ifremer.coser.bean.Haul; +import fr.ifremer.coser.bean.Length; +import fr.ifremer.coser.bean.Project; +import fr.ifremer.coser.bean.Strata; + +/** + * Command pattern object. + * + * @author chatellier + * @version $Revision$ + * + * Last update : $Date$ + * By : $Author$ + */ +public class ModifyFieldCommand extends Command { + + /** Category de données sur lequel porte l'action. */ + protected Category category; + + /** Line index of object to do command to. */ + protected int line; + + /** Field index to to command. */ + protected int fieldIndex; + + /** Field actual value. */ + protected Object currentValue; + + /** Field new value. */ + protected Object newValue; + + public Category getCategory() { + return category; + } + + public void setCategory(Category category) { + this.category = category; + } + + public int getLine() { + return line; + } + + public void setLine(int line) { + this.line = line; + } + + public int getFieldIndex() { + return fieldIndex; + } + + public void setFieldIndex(int fieldIndex) { + this.fieldIndex = fieldIndex; + } + + public Object getCurrentValue() { + return currentValue; + } + + public void setCurrentValue(Object currentValue) { + this.currentValue = currentValue; + } + + public Object getNewValue() { + return newValue; + } + + public void setNewValue(Object newValue) { + this.newValue = newValue; + } + + @Override + public void doCommand(Project project) throws CoserBusinessException { + + } + + @Override + public void undoCommand(Project project) throws CoserBusinessException { + + } + + @Override + public String toString() { + String toString = "Modify field "; + switch (category) { + case CATCH: + toString += Catch.EN_HEADERS[fieldIndex]; + break; + case HAUL: + toString += Haul.EN_HEADERS[fieldIndex]; + break; + case LENGTH: + toString += Length.EN_HEADERS[fieldIndex]; + break; + case STRATA: + toString += Strata.EN_HEADERS[fieldIndex]; + break; + } + toString += " on line " + line; + return toString; + } +} Property changes on: trunk/coser-business/src/main/java/fr/ifremer/coser/command/ModifyFieldCommand.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision HeadURL Added: trunk/coser-business/src/main/java/fr/ifremer/coser/services/CommandService.java =================================================================== --- trunk/coser-business/src/main/java/fr/ifremer/coser/services/CommandService.java (rev 0) +++ trunk/coser-business/src/main/java/fr/ifremer/coser/services/CommandService.java 2010-10-26 13:59:58 UTC (rev 103) @@ -0,0 +1,112 @@ +/* + * #%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 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 java.util.ListIterator; +import java.util.UUID; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import fr.ifremer.coser.CoserBusinessConfig; +import fr.ifremer.coser.CoserBusinessException; +import fr.ifremer.coser.bean.Project; +import fr.ifremer.coser.command.Command; + +/** + * Command service. + * + * @author chatellier + * @version $Revision$ + * + * Last update : $Date$ + * By : $Author$ + */ +public class CommandService { + + private static final Log log = LogFactory.getLog(CommandService.class); + + protected CoserBusinessConfig config; + + public CommandService(CoserBusinessConfig config) { + this.config = config; + } + + /** + * Generate new unique command UUID. + * + * @return unique command uuid + */ + public String getUniqueCommandUUID() { + return UUID.randomUUID().toString(); + } + + /** + * Perform command on project. Save action on project history file. + * + * @param command command to perform + * @param project project + * @throws CoserBusinessException + */ + public void doAction(Command command, Project project) throws CoserBusinessException { + if (log.isDebugEnabled()) { + log.debug("Do action " + command); + } + + // si la commande n'a pas de UUID + if (command.getCommandUUID() == null) { + command.setCommandUUID(getUniqueCommandUUID()); + } + + command.doCommand(project); + project.getHistoryCommand().add(command); + } + + /** + * Undo last command on project. + * + * @param project project + * @throws CoserBusinessException + */ + public void undoAction(Project project) throws CoserBusinessException { + + ListIterator<Command> itCommand = project.getHistoryCommand().listIterator(project.getHistoryCommand().size()); + + String lastUUID = null; + while (itCommand.hasPrevious()) { + Command command = itCommand.previous(); + + if (lastUUID != null && !lastUUID.equals(command.getCommandUUID())) { + break; + } + else { + command.undoCommand(project); + lastUUID = command.getCommandUUID(); + itCommand.remove(); + } + } + } +} Property changes on: trunk/coser-business/src/main/java/fr/ifremer/coser/services/CommandService.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision HeadURL Modified: trunk/coser-business/src/main/java/fr/ifremer/coser/services/ImportService.java =================================================================== --- trunk/coser-business/src/main/java/fr/ifremer/coser/services/ImportService.java 2010-10-26 08:53:49 UTC (rev 102) +++ trunk/coser-business/src/main/java/fr/ifremer/coser/services/ImportService.java 2010-10-26 13:59:58 UTC (rev 103) @@ -88,7 +88,7 @@ * @return project * @throws CoserBusinessException */ - public Project loadCSVFile(Project project, Category category, File file, boolean originalLoading) throws CoserBusinessException { + public DataStorage loadCSVFile(Project project, Category category, File file, boolean originalLoading) throws CoserBusinessException { DataStorage content = new MemoryListStorage(); @@ -127,8 +127,6 @@ content.add(line); } } - - addProjectContent(project, category, content); } catch (FileNotFoundException ex) { throw new CoserBusinessException("Can't read file", ex); } catch (IOException ex) { @@ -139,39 +137,10 @@ IOUtils.closeQuietly(reader); } - return project; + return content; } /** - * Set content into project depending on category type. - * - * @param project project - * @param category category - * @param content content to set - */ - protected void addProjectContent(Project project, Category category, - DataStorage content) { - - switch (category) { - case CATCH: - project.setCatch(content); - break; - case HAUL: - project.setHaul(content); - break; - case LENGTH: - project.setLength(content); - break; - case STRATA: - project.setStrata(content); - break; - case REFTAX_SPECIES: - project.setRefTaxSpecies(content); - break; - } - } - - /** * Check csv file header names and order depending on file category. * * @param category @@ -223,32 +192,43 @@ } /** - * Store project category data in specified file as csv. + * Get empty storage for category (filled with just header) * - * @param project project containing data - * @param category category to save - * @param file file to save to - * @throws CoserBusinessException + * @param project project + * @param category category + * @return initialized storage */ - public void storeData(Project project, Category category, File file) throws CoserBusinessException { + public DataStorage getEmptyStorage(Project project, Category category) { - // get project category content - DataStorage content = null; + DataStorage dataStorage = new MemoryListStorage(); switch (category) { case CATCH: - content = project.getCatch(); + dataStorage.add(Catch.EN_HEADERS); break; case HAUL: - content = project.getHaul(); + dataStorage.add(Haul.EN_HEADERS); break; case LENGTH: - content = project.getLength(); + dataStorage.add(Length.EN_HEADERS); break; case STRATA: - content = project.getStrata(); + dataStorage.add(Strata.EN_HEADERS); break; } + return dataStorage; + } + + /** + * Store project category data in specified file as csv. + * + * @param project project containing data + * @param content content to save + * @param file file to save to + * @throws CoserBusinessException + */ + public void storeData(Project project, DataStorage content, File file) throws CoserBusinessException { + // save content Writer writer = null; CSVWriter csvWriter = null; Modified: trunk/coser-business/src/main/java/fr/ifremer/coser/services/ProjectService.java =================================================================== --- trunk/coser-business/src/main/java/fr/ifremer/coser/services/ProjectService.java 2010-10-26 08:53:49 UTC (rev 102) +++ trunk/coser-business/src/main/java/fr/ifremer/coser/services/ProjectService.java 2010-10-26 13:59:58 UTC (rev 103) @@ -50,6 +50,10 @@ import fr.ifremer.coser.bean.Haul; import fr.ifremer.coser.bean.Project; import fr.ifremer.coser.bean.Specy; +import fr.ifremer.coser.command.Command; +import fr.ifremer.coser.command.DeleteLineCommand; +import fr.ifremer.coser.command.MergeSpeciesCommand; +import fr.ifremer.coser.storage.DataStorage; /** * Service business method relative to project. @@ -67,10 +71,13 @@ protected CoserBusinessConfig config; protected ImportService importService; + + protected CommandService commandService; public ProjectService(CoserBusinessConfig config) { this.config = config; importService = new ImportService(config); + commandService = new CommandService(config); } /** @@ -97,7 +104,7 @@ * @throws CoserBusinessException */ public Project loadProject(String projectName) throws CoserBusinessException { - + // check project existence File projectsDirectory = config.getProjectsDirectory(); File projectDirectory = new File(projectsDirectory, projectName); @@ -112,15 +119,14 @@ int fileLoaded = 0; for (Category category : Category.values()) { - // les fichiers de data sont dans le dossier "original" + // seulement les category de données ici if (category.isDataCategory()) { - File inputFile = new File(controlDirectory, - category.getStorageFileName() + - CoserConstants.STORAGE_CONTROL_SUFFIX + - CoserConstants.STORAGE_CSV_EXTENSION); + File inputFile = new File(controlDirectory, category.getStorageFileName() + + CoserConstants.STORAGE_CONTROL_SUFFIX + CoserConstants.STORAGE_CSV_EXTENSION); if (inputFile.isFile()) { - project = importService.loadCSVFile(project, category, inputFile, false); + DataStorage dataStorage = importService.loadCSVFile(project, category, inputFile, false); + addProjectContent(project, category, dataStorage, false); fileLoaded++; } else { @@ -128,6 +134,21 @@ log.debug("Can't find file " + inputFile); } } + + // load deleted file if exists + inputFile = new File(controlDirectory, category.getStorageFileName() + + CoserConstants.STORAGE_DELECTED_SUFFIX + CoserConstants.STORAGE_CSV_EXTENSION); + DataStorage dataStorage = null; + if (inputFile.isFile()) { + dataStorage = importService.loadCSVFile(project, category, inputFile, false); + } + else { + dataStorage = importService.getEmptyStorage(project, category); + if (log.isDebugEnabled()) { + log.debug("Can't find file " + inputFile); + } + } + addProjectContent(project, category, dataStorage, true); } } @@ -139,15 +160,21 @@ if (category.isDataCategory()) { File storageDataFile = new File(originalDirectory, category.getStorageFileName() + CoserConstants.STORAGE_CSV_EXTENSION); - + + // main data if (storageDataFile.isFile()) { - project = importService.loadCSVFile(project, category, storageDataFile, true); + DataStorage dataStorage = importService.loadCSVFile(project, category, storageDataFile, true); + addProjectContent(project, category, dataStorage, false); } else { // si on arrive ici et qu'un fichier original // n'existe pas, c'est grave throw new CoserBusinessException(_("Missing file %s", storageDataFile)); } + + // deleted data + DataStorage dataStorage = importService.getEmptyStorage(project, category); + addProjectContent(project, category, dataStorage, true); } } } @@ -158,7 +185,8 @@ CoserConstants.STORAGE_CSV_EXTENSION); if (inputFile.isFile()) { - project = importService.loadCSVFile(project, Category.REFTAX_SPECIES, inputFile, true); + DataStorage dataStorage = importService.loadCSVFile(project, Category.REFTAX_SPECIES, inputFile, true); + addProjectContent(project, Category.REFTAX_SPECIES, dataStorage, false); } else { // si on arrive ici et qu'un fichier original @@ -166,10 +194,63 @@ throw new CoserBusinessException(_("Missing file %s", inputFile)); } + // init des autres resources du projet + project.setHistoryCommand(new ArrayList<Command>()); + return project; } /** + * Set content into project depending on category type. + * + * @param project project + * @param category category + * @param content content to set + * @param deletedContent if content means deleted objects for {@code category} + */ + protected void addProjectContent(Project project, Category category, + DataStorage content, boolean deletedContent) { + + switch (category) { + case CATCH: + if (!deletedContent) { + project.setCatch(content); + } + else { + project.setDeletedCatch(content); + } + break; + case HAUL: + if (!deletedContent) { + project.setHaul(content); + } + else { + project.setDeletedHaul(content); + } + break; + case LENGTH: + if (!deletedContent) { + project.setLength(content); + } + else { + project.setDeletedLength(content); + } + break; + case STRATA: + if (!deletedContent) { + project.setStrata(content); + } + else { + project.setDeletedStrata(content); + } + break; + case REFTAX_SPECIES: + project.setRefTaxSpecies(content); + break; + } + } + + /** * Create new project. * * Do (ordered): @@ -202,7 +283,14 @@ for (Map.Entry<Category, File> categoryAndFile : categoriesAndFiles.entrySet()) { Category category = categoryAndFile.getKey(); File dataFile = categoryAndFile.getValue(); - project = importService.loadCSVFile(project, category, dataFile, true); + DataStorage dataStorage = importService.loadCSVFile(project, category, dataFile, true); + addProjectContent(project, category, dataStorage, false); + + if (category.isDataCategory()) { + // deleted data + DataStorage dataStorage2 = importService.getEmptyStorage(project, category); + addProjectContent(project, category, dataStorage2, true); + } } // create new project directory @@ -242,6 +330,9 @@ } } + // init des autres resources du projet + project.setHistoryCommand(new ArrayList<Command>()); + return project; } @@ -263,6 +354,8 @@ controlDirectory.mkdirs(); for (Category category : Category.values()) { if (category.isDataCategory()) { + + // save main content File controlFile = new File(controlDirectory, category.getStorageFileName() + CoserConstants.STORAGE_CONTROL_SUFFIX + @@ -270,12 +363,77 @@ if (log.isDebugEnabled()) { log.debug("Saving control file : " + controlFile); } - importService.storeData(project, category, controlFile); + DataStorage content = getProjectContent(project, category, false); + importService.storeData(project, content, controlFile); + + // save deleted content (if needed) + DataStorage contentDeleted = getProjectContent(project, category, true); + // if more content than header + if (contentDeleted.size() > 1) { + File deletedDataFile = new File(controlDirectory, + category.getStorageFileName() + + CoserConstants.STORAGE_DELECTED_SUFFIX + + CoserConstants.STORAGE_CSV_EXTENSION); + importService.storeData(project, contentDeleted, deletedDataFile); + } } } } /** + * Set content into project depending on category type. + * + * @param project project + * @param category category + * @param content content to set + * @param deletedContent if content means deleted objects for {@code category} + */ + protected DataStorage getProjectContent(Project project, Category category, boolean deletedContent) { + + DataStorage content = null; + + switch (category) { + case CATCH: + if (!deletedContent) { + content = project.getCatch(); + } + else { + content = project.getDeletedCatch(); + } + break; + case HAUL: + if (!deletedContent) { + content = project.getHaul(); + } + else { + content = project.getDeletedHaul(); + } + break; + case LENGTH: + if (!deletedContent) { + content = project.getLength(); + } + else { + content = project.getDeletedLength(); + } + break; + case STRATA: + if (!deletedContent) { + content = project.getStrata(); + } + else { + content = project.getDeletedStrata(); + } + break; + case REFTAX_SPECIES: + project.setRefTaxSpecies(content); + break; + } + + return content; + } + + /** * Save project selection. * * @param project project to save selection @@ -304,7 +462,9 @@ if (log.isDebugEnabled()) { log.debug("Saving selection file : " + controlFile); } - importService.storeData(project, category, controlFile); + + DataStorage content = getProjectContent(project, category, false); + importService.storeData(project, content, controlFile); } } @@ -457,131 +617,28 @@ throw new CoserBusinessException(_("Specy %s doesn't exist in referential", newSpecyName)); } - project = mergeCatch(project, newSpecyName, speciesNames); - project = mergeLength(project, newSpecyName, speciesNames); - return project; - } + MergeSpeciesCommand command = new MergeSpeciesCommand(); + command.setNewSpecyName(newSpecyName); + command.setSpeciesNames(speciesNames); + commandService.doAction(command, project); - /** - * Fusion d'espece dans les données de captures. - * - * @param project project - * @param newSpecyName new specy name (after merge) - * @param speciesNames species name to merge - * @return project - */ - protected Project mergeLength(Project project, String newSpecyName, - String... speciesNames) { return project; } /** - * Fusion d'especes dans les données de taille. + * Supprime une données via son index. * * @param project project - * @param newSpecyName new specy name (after merge) - * @param speciesNames species name to merge - * @return project + * @param category category + * @param index index to delete + * @throws CoserBusinessException */ - protected Project mergeCatch(Project project, String newSpecyName, - String... speciesNames) { - - // "Campagne","Annee","Trait","Espece","Nombre","Poids" + public void deleteData(Project project, Category category, String index) throws CoserBusinessException { - // regroupement par campagne/annee/trait - String lastCampagne = null; - String lastAnnee = null; - String lastTraits = null; - String[] tupleFound = null; - int tupleIndex = -1; - - // parcours des elements - Iterator<String[]> itTuple = project.getCatch().iterator(); - itTuple.next(); // skip header - int index = 1; // skip header - while (itTuple.hasNext()) { - String[] tuple = itTuple.next(); - - // si les valeurs servant au bornes de regroupement - // on changer, on reset les données du merge - String currentCampagne = tuple[Catch.INDEX_SURVEY]; - String currentAnnee = tuple[Catch.INDEX_YEAR]; - String currentTraits = tuple[Catch.INDEX_HAUL]; - if (!currentCampagne.equals(lastCampagne) || - !currentAnnee.equals(lastAnnee) || - !currentTraits.equals(lastTraits)) { - - tupleFound = null; - lastCampagne = currentCampagne; - lastAnnee = currentAnnee; - lastTraits = currentTraits; - } - - // test si l'espece en cours fait partie de celle a merger - String species = tuple[Catch.INDEX_SPECIES]; - boolean specyFound = false; - for (String specy : speciesNames) { - if (specy.equals(species)) { - specyFound = true; - } - } - - // si l'espece est a merger, on se souvient du tuple - // principale a merge, ou on merge avec le tuple - // principal si on a deja un tuple principal - if (specyFound) { - if (tupleFound == null) { - tupleFound = tuple; - tupleFound[3] = newSpecyName; - tupleIndex = index; - } - else { - tupleFound = mergeCatches(tupleFound, tuple); - // et on supprime le tuple - // qui a ete merge - itTuple.remove(); - // bidouille le remove decalle les index suivants - index--; - } - - project.getCatch().set(tupleIndex, tupleFound); - } - - index++; - } - - return project; + // create new delete action + DeleteLineCommand command = new DeleteLineCommand(); + command.setCategory(category); + command.setLineIndex(index); + commandService.doAction(command, project); } - - /** - * Merge deux lines des catch. - * - * Le resultat est mergé dans {@code tuple1} et retourné. - * - * Somme les "Nombre" et "Poids" - * - * @param tuple1 tuple1 - * @param tuple2 tuple2 - * @return tuple1 - */ - protected String[] mergeCatches(String[] tuple1, String[] tuple2) { - - // "Campagne","Annee","Trait","Espece","Nombre","Poids" - - try { - double nombre1 = Double.parseDouble(tuple1[Catch.INDEX_NUMBER]); - double nombre2 = Double.parseDouble(tuple2[Catch.INDEX_NUMBER]); - double poids1 = Double.parseDouble(tuple1[Catch.INDEX_WEIGHT]); - double poids2 = Double.parseDouble(tuple2[Catch.INDEX_WEIGHT]); - - tuple1[Catch.INDEX_NUMBER] = String.valueOf(nombre1 + nombre2); - tuple1[Catch.INDEX_WEIGHT] = String.valueOf(poids1 + poids2); - } - catch (NumberFormatException ex) { - if (log.isWarnEnabled()) { - log.warn("Can't convert data as double for merge", ex); - } - } - return tuple1; - } } Modified: trunk/coser-business/src/main/java/fr/ifremer/coser/storage/DataStorage.java =================================================================== --- trunk/coser-business/src/main/java/fr/ifremer/coser/storage/DataStorage.java 2010-10-26 08:53:49 UTC (rev 102) +++ trunk/coser-business/src/main/java/fr/ifremer/coser/storage/DataStorage.java 2010-10-26 13:59:58 UTC (rev 103) @@ -47,6 +47,14 @@ * @param data data to add */ void add(String[] data); + + /** + * Add new data into storage. + * + * @param index index to insert data + * @param data data to add + */ + void add(int index, String[] data); /** * Get data at specified index. Modified: trunk/coser-business/src/main/java/fr/ifremer/coser/storage/MemoryListStorage.java =================================================================== --- trunk/coser-business/src/main/java/fr/ifremer/coser/storage/MemoryListStorage.java 2010-10-26 08:53:49 UTC (rev 102) +++ trunk/coser-business/src/main/java/fr/ifremer/coser/storage/MemoryListStorage.java 2010-10-26 13:59:58 UTC (rev 103) @@ -109,6 +109,16 @@ String stringData = arrayToString(data); internalStorage.add(stringData); } + + /* + * @see fr.ifremer.coser.storage.DataStorage#addData(java.lang.String[]) + */ + @Override + public void add(int index, String[] data) { + + String stringData = arrayToString(data); + internalStorage.add(index, stringData); + } protected String arrayToString(String[] data) { StringWriter writer = new StringWriter(); @@ -164,4 +174,9 @@ String[] oldArray = stringToArray(old); return oldArray; } + + @Override + public String toString() { + return internalStorage.toString(); + } } Added: trunk/coser-business/src/test/java/fr/ifremer/coser/services/CommandServiceTest.java =================================================================== --- trunk/coser-business/src/test/java/fr/ifremer/coser/services/CommandServiceTest.java (rev 0) +++ trunk/coser-business/src/test/java/fr/ifremer/coser/services/CommandServiceTest.java 2010-10-26 13:59:58 UTC (rev 103) @@ -0,0 +1,114 @@ +/* + * #%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 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 junit.framework.Assert; + +import org.junit.Before; +import org.junit.Test; + +import fr.ifremer.coser.CoserBusinessException; +import fr.ifremer.coser.CoserConstants.Category; +import fr.ifremer.coser.bean.Project; +import fr.ifremer.coser.command.DeleteLineCommand; + +/** + * CommandService tests + * + * @author chatellier + * @version $Revision$ + * + * Last update : $Date$ + * By : $Author$ + */ +public class CommandServiceTest extends CoserTestAbstract { + + protected CommandService commandService; + protected ProjectService projectService; + + @Before + public void initService() { + projectService = new ProjectService(config); + commandService = new CommandService(config); + } + + /** + * Test la suppression correcte d'une ligne. + * + * @throws CoserBusinessException + */ + @Test + public void testDeleteLine() throws CoserBusinessException { + Project project = createTestProject(projectService); + Assert.assertEquals(29, project.getLength().size()); + + DeleteLineCommand command = new DeleteLineCommand(); + command.setLineIndex("2"); + command.setCategory(Category.LENGTH); + + commandService.doAction(command, project); + Assert.assertEquals(28, project.getLength().size()); + } + + /** + * Test la suppression incorrecte d'une ligne. + * + * @throws CoserBusinessException + */ + @Test(expected=CoserBusinessException.class) + public void testDeleteLineWrong() throws CoserBusinessException { + Project project = createTestProject(projectService); + DeleteLineCommand command = new DeleteLineCommand(); + command.setLineIndex("2"); + command.setCategory(Category.LENGTH); + commandService.doAction(command, project); + commandService.doAction(command, project); + } + + /** + * Test la suppression correcte d'une ligne puis le rétablissement + * de la ligne supprimée. + * + * @throws CoserBusinessException + */ + @Test + public void testDeleteLineUndo() throws CoserBusinessException { + Project project = createTestProject(projectService); + Assert.assertEquals("3", project.getLength().get(4)[0]); + + DeleteLineCommand command = new DeleteLineCommand(); + command.setLineIndex("3"); + command.setCategory(Category.LENGTH); + + commandService.doAction(command, project); + System.out.println(project.getLength()); + Assert.assertEquals("4", project.getLength().get(4)[0]); + commandService.undoAction(project); + System.out.println("\n" + project.getLength()); + Assert.assertEquals("3", project.getLength().get(4)[0]); + + } +} Property changes on: trunk/coser-business/src/test/java/fr/ifremer/coser/services/CommandServiceTest.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision HeadURL Modified: trunk/coser-business/src/test/java/fr/ifremer/coser/services/CoserTestAbstract.java =================================================================== --- trunk/coser-business/src/test/java/fr/ifremer/coser/services/CoserTestAbstract.java 2010-10-26 08:53:49 UTC (rev 102) +++ trunk/coser-business/src/test/java/fr/ifremer/coser/services/CoserTestAbstract.java 2010-10-26 13:59:58 UTC (rev 103) @@ -31,6 +31,8 @@ import java.util.Map; import org.apache.commons.io.FileUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.junit.AfterClass; import org.junit.BeforeClass; @@ -50,6 +52,8 @@ */ public abstract class CoserTestAbstract { + private static final Log log = LogFactory.getLog(CoserTestAbstract.class); + protected static CoserBusinessConfig config; protected static File testDirectory; @@ -67,7 +71,7 @@ @AfterClass public static void cleanDirectory() throws IOException { - FileUtils.deleteDirectory(testDirectory); + //FileUtils.deleteDirectory(testDirectory); } protected Project createTestProject(ProjectService service) throws CoserBusinessException { @@ -89,6 +93,10 @@ categoriesAndFile.put(Category.REFTAX_SPECIES, testReftax); project = service.createProject(project, categoriesAndFile); + + if (log.isDebugEnabled()) { + log.debug("Created project : " + project.getName()); + } return project; } } Modified: trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/control/ControlDataTableModel.java =================================================================== --- trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/control/ControlDataTableModel.java 2010-10-26 08:53:49 UTC (rev 102) +++ trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/control/ControlDataTableModel.java 2010-10-26 13:59:58 UTC (rev 103) @@ -103,15 +103,12 @@ @Override public Object getValueAt(int rowIndex, int columnIndex) { - Object result = null; + Object result = getDataAt(rowIndex)[columnIndex]; - if (columnIndex == 0) { - result = rowIndex + 1; - } - else { - result = data.get(rowIndex + 1)[columnIndex]; - } - return result; } + + public String[] getDataAt(int rowIndex) { + return data.get(rowIndex + 1); + } } Added: trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/control/ControlFindReplaceDialog.jaxx =================================================================== --- trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/control/ControlFindReplaceDialog.jaxx (rev 0) +++ trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/control/ControlFindReplaceDialog.jaxx 2010-10-26 13:59:58 UTC (rev 103) @@ -0,0 +1,53 @@ +<!-- + #%L + Coser :: UI + + $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% + --> +<JDialog title="coser.ui.control.replace.title" modal="true"> + <ControlHandler id="handler" javaBean="null" /> + <Table> + <row> + <cell fill="horizontal"> + <JLabel text="coser.ui.control.replace.find" /> + </cell> + <cell weightx="1" fill="horizontal"> + <JTextField id="replaceFindField" /> + </cell> + </row> + <row> + <cell fill="horizontal"> + <JLabel text="coser.ui.control.replace.replace" /> + </cell> + <cell weightx="2" weighty="1" fill="both" columns="3"> + <JTextField id="replaceReplaceField" /> + </cell> + </row> + <row> + <cell columns="4" anchor="east" insets="0"> + <JPanel> + <JButton text="coser.ui.common.cancel" onActionPerformed="dispose()"/> + <JButton text="coser.ui.common.valid" onActionPerformed="getHandler().performFindAndReplace(this)" /> + </JPanel> + </cell> + </row> + </Table> +</JDialog> 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-10-26 08:53:49 UTC (rev 102) +++ trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/control/ControlHandler.java 2010-10-26 13:59:58 UTC (rev 103) @@ -30,13 +30,17 @@ import java.awt.Point; import java.awt.Rectangle; import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseEvent; import java.io.File; import java.io.IOException; import java.util.List; import javax.swing.JLabel; +import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; +import javax.swing.JPopupMenu; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.JViewport; @@ -44,6 +48,7 @@ import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.event.ListSelectionEvent; +import javax.swing.table.TableColumnModel; import jaxx.runtime.SwingUtil; import jaxx.runtime.validator.swing.SwingValidator; @@ -91,6 +96,51 @@ } /** + * Affiche le menu contextuel de la table qui contient les données. + * + * @param view view + * @param event mouse event + */ + public void showDataTableContextMenu(final ControlView view, MouseEvent event) { + + // clic contextuel + if (event.getButton() == MouseEvent.BUTTON3) { + int[] dataSelectedRows = view.getControlDataTable().getSelectedRows(); + int columnIndex = view.getControlDataTable().getColumnModel().getColumnIndexAtX(event.getX()); + + // plusieurs lignes selectionnées et pas la premiere colonne (Line index) + if (dataSelectedRows.length > 0 && columnIndex > 0) { + String columnName = view.getControlDataTable().getColumnName(columnIndex); + JPopupMenu popupMenu = new JPopupMenu(_("coser.ui.control.dataMenuLabel")); + JMenuItem replaceMenu = new JMenuItem(_("coser.ui.control.dataMenuReplace", columnName)); + replaceMenu.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + ControlFindReplaceDialog viewDialog = new ControlFindReplaceDialog(view); + viewDialog.setLocationRelativeTo(view); + viewDialog.setVisible(true); + } + }); + popupMenu.add(replaceMenu); + popupMenu.show(view.getControlDataTable(), event.getX(), event.getY()); + } + } + } + + /** + * Perform find and replace. + * + * @param view parent view + */ + public void performFindAndReplace(ControlFindReplaceDialog view) { + String find = view.getReplaceFindField().getText(); + String replace = view.getReplaceReplaceField().getText(); + if (log.isWarnEnabled()) { + log.warn("Perform : " + find + " replace with " + replace); + } + } + + /** * Check project. * * @param view @@ -323,7 +373,16 @@ validator.installUIs(); validator.reloadBean(); + + view.getControlDataValidButton().setEnabled(true); + view.getControlDataCancelButton().setEnabled(true); + view.getControlDataDeleteButton().setEnabled(true); } + else { + view.getControlDataValidButton().setEnabled(false); + view.getControlDataCancelButton().setEnabled(false); + view.getControlDataDeleteButton().setEnabled(false); + } view.getEditionScrollPane().repaint(); view.getEditionScrollPane().validate(); } @@ -423,33 +482,25 @@ public void deleteData(ControlView view) { int response = JOptionPane.showConfirmDialog(view, _("coser.ui.control.confirmDeletionMessage"), _("coser.ui.control.confirmDeletionTitle"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); - + if (response == JOptionPane.YES_OPTION) { Project project = view.getContextValue(Project.class); + ProjectService projectService = view.getContextValue(ProjectService.class); + + // get selected row, and selected csv line index JTable controlDataTable = view.getControlDataTable(); ControlDataTableModel model = (ControlDataTableModel)controlDataTable.getModel(); int selectedLine = controlDataTable.getSelectedRow(); Category category = (Category)view.getCategoryComboBox().getSelectedItem(); - - // get data - switch (category) { - case CATCH: - project.getCatch().remove(selectedLine + 1); // csv header + String[] data = model.getDataAt(selectedLine); + + try { + projectService.deleteData(project, category, data[AbstractDataEntity.INDEX_LINE]); model.fireTableRowsDeleted(selectedLine, selectedLine); - break; - case HAUL: - project.getHaul().remove(selectedLine + 1); // csv header - model.fireTableRowsDeleted(selectedLine, selectedLine); - break; - case LENGTH: - project.getLength().remove(selectedLine + 1); // csv header - model.fireTableRowsDeleted(selectedLine, selectedLine); - break; - case STRATA: - project.getCatch().remove(selectedLine + 1); // csv header - model.fireTableRowsDeleted(selectedLine, selectedLine); - break; } + catch (CoserBusinessException ex) { + throw new CoserException("Can't delete data", ex); + } } } } 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-10-26 08:53:49 UTC (rev 102) +++ trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/control/ControlView.jaxx 2010-10-26 13:59:58 UTC (rev 103) @@ -46,7 +46,8 @@ <row> <cell fill="both" weightx="1" weighty="2" columns="2"> <JScrollPane> - <JTable id='controlDataTable' model="{new fr.ifremer.coser.ui.control.ControlDataTableModel(this)}" /> + <JTable id='controlDataTable' model="{new fr.ifremer.coser.ui.control.ControlDataTableModel(this)}" + onMouseClicked="getHandler().showDataTableContextMenu(this, event)"/> <ListSelectionModel id="controlDataTableSelectionModel" javaBean="controlDataTable.getSelectionModel()" onValueChanged="getHandler().controlDataTableSelectionChanged(this, event)" /> </JScrollPane> @@ -63,16 +64,19 @@ </row> <row> <cell weightx="1" anchor="east"> - <JButton text="coser.ui.common.valid" - onActionPerformed="getHandler().validDataModification(this)" /> + <JButton id="controlDataValidButton" text="coser.ui.common.valid" + onActionPerformed="getHandler().validDataModification(this)" + enabled="false" /> </cell> <cell> - <JButton text="coser.ui.common.cancel" - onActionPerformed="getHandler().cancelDataModification(this)" /> + <JButton id="controlDataCancelButton" text="coser.ui.common.cancel" + onActionPerformed="getHandler().cancelDataModification(this)" + enabled="false" /> </cell> <cell> - <JButton text="coser.ui.common.delete" - onActionPerformed="getHandler().deleteData(this)" /> + <JButton id="controlDataDeleteButton" text="coser.ui.common.delete" + onActionPerformed="getHandler().deleteData(this)" + enabled="false" /> </cell> </row> </Table> Modified: trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/selection/SelectionListsView.jaxx =================================================================== --- trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/selection/SelectionListsView.jaxx 2010-10-26 08:53:49 UTC (rev 102) +++ trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/selection/SelectionListsView.jaxx 2010-10-26 13:59:58 UTC (rev 103) @@ -57,7 +57,7 @@ <row> <cell /> <cell /> - <cell fill="horizontal" insets="0" columns="2"> + <cell fill="horizontal" insets="0" columns="2" anchor="west"> <JPanel> <JLabel text="coser.ui.selection.filter.occurrence" /> <JTextField id="selectionFilterOccurrenceField" columns="3" text="5" /> @@ -66,7 +66,12 @@ <JButton text="coser.ui.selection.filter.filter" /> </JPanel> </cell> - <cell columns="4"/> + <cell columns="2"> + <JButton icon="chart_bar.png" /> + </cell> + <cell columns="2"> + <JButton icon="chart_bar.png" /> + </cell> </row> <row> <cell weighty="1" weightx="1" fill="both" columns="2"> Modified: trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/selection/SelectionResultView.jaxx =================================================================== --- trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/selection/SelectionResultView.jaxx 2010-10-26 08:53:49 UTC (rev 102) +++ trunk/coser-ui/src/main/java/fr/ifremer/coser/ui/selection/SelectionResultView.jaxx 2010-10-26 13:59:58 UTC (rev 103) @@ -24,12 +24,44 @@ --> <Table> <SelectionHandler id="handler" javaBean="null" /> - <row> - <cell weightx="1" weighty="1" fill="both"> - <JScrollPane> - <JTable model="{new fr.ifremer.coser.ui.result.ResultTableModel()}"/> - </JScrollPane> + <cell weightx="1" fill="horizontal"> + <Table border='{BorderFactory.createTitledBorder(_("coser.ui.result.extractDataTitle"))}'> + <row> + <cell> + <JLabel text="coser.ui.result.extractDataLabel" /> + </cell> + <cell weightx="1" fill="horizontal"> + <JTextField id="resultExtractDataField" /> + </cell> + <cell> + <JButton text="coser.ui.common.selectFile" /> + </cell> + </row> + <row> + <cell columns="3"> + <JButton text="coser.ui.result.extractDataButton" /> + </cell> + </row> + </Table> </cell> </row> + <row> + <cell weightx="1" weighty="2" fill="both"> + <Table border='{BorderFactory.createTitledBorder(_("coser.ui.result.availableDataTitle"))}'> + <row> + <cell weightx="1" weighty="1" fill="both"> + <JScrollPane> + <JTable model="{new fr.ifremer.coser.ui.result.ResultTableModel()}"/> + </JScrollPane> + </cell> + </row> + <row> + <cell> + + </cell> + </row> + </Table> + </cell> + </row> </Table> Modified: trunk/coser-ui/src/main/resources/i18n/coser-ui-en_GB.properties =================================================================== --- trunk/coser-ui/src/main/resources/i18n/coser-ui-en_GB.properties 2010-10-26 08:53:49 UTC (rev 102) +++ trunk/coser-ui/src/main/resources/i18n/coser-ui-en_GB.properties 2010-10-26 13:59:58 UTC (rev 103) @@ -1,7 +1,3 @@ -Name= -Selection\ 1= -Selection\ 2= -Selection\ 3= coser.config.application.version.description=Application's version coser.config.category.main= coser.config.category.main.description= @@ -13,7 +9,7 @@ coser.config.reference.zones.description= coser.config.support.email.description=Support email address coser.config.validator.directory.description= -coser.ui.common.cancel= +coser.ui.common.cancel=Cancel coser.ui.common.comment=Comment coser.ui.common.delete= coser.ui.common.selectAll= @@ -23,10 +19,15 @@ coser.ui.config.title= coser.ui.control.confirmDeletionMessage= coser.ui.control.confirmDeletionTitle= +coser.ui.control.dataMenuLabel= +coser.ui.control.dataMenuReplace= coser.ui.control.global.field= coser.ui.control.global.level= coser.ui.control.global.message= coser.ui.control.graphtitle=Graph +coser.ui.control.replace.find= +coser.ui.control.replace.replace= +coser.ui.control.replace.title= coser.ui.control.save= coser.ui.error.htmlmessage=An error occurs \: %s coser.ui.error.title=Global application error @@ -62,6 +63,9 @@ coser.ui.project.traitsFile=Traits file \: coser.ui.project.useCustomReferenceSpeciesFile= coser.ui.project.usedReferenceSpeciesFile= +coser.ui.result.extractDataButton= +coser.ui.result.extractDataLabel= +coser.ui.result.extractDataTitle= coser.ui.selection.allSpecies=All species coser.ui.selection.details.beginDate= coser.ui.selection.details.createSelection= @@ -74,9 +78,9 @@ coser.ui.selection.details.species= coser.ui.selection.details.technicalComment= coser.ui.selection.details.zone= -coser.ui.selection.filter.density=X\: +coser.ui.selection.filter.density=Density \: coser.ui.selection.filter.filter=Filter -coser.ui.selection.filter.occurrence=Y\: +coser.ui.selection.filter.occurrence=Occurrence \: coser.ui.selection.fusion.comment= coser.ui.selection.fusion.description= coser.ui.selection.fusion.name= Modified: trunk/coser-ui/src/main/resources/i18n/coser-ui-fr_FR.properties =================================================================== --- trunk/coser-ui/src/main/resources/i18n/coser-ui-fr_FR.properties 2010-10-26 08:53:49 UTC (rev 102) +++ trunk/coser-ui/src/main/resources/i18n/coser-ui-fr_FR.properties 2010-10-26 13:59:58 UTC (rev 103) @@ -1,7 +1,3 @@ -Name= -Selection\ 1= -Selection\ 2= -Selection\ 3= coser.config.application.version.description=Version de l'application coser.config.category.main=Coser coser.config.category.main.description=Configuration principale @@ -19,12 +15,17 @@ coser.ui.config.title=Configuration coser.ui.control.confirmDeletionMessage=\u00CAtes vous s\u00FBr de vouloir supprimer cette donn\u00E9e ? coser.ui.control.confirmDeletionTitle=Confirmation de suppression +coser.ui.control.dataMenuLabel=Menu table des donn\u00E9es +coser.ui.control.dataMenuReplace=Remplacer dans %s coser.ui.control.global.field=Champ coser.ui.control.global.level=Type coser.ui.control.global.message=Message coser.ui.control.graphtitle=Graphique +coser.ui.control.replace.find=Chercher \: +coser.ui.control.replace.replace=Remplacer \: +coser.ui.control.replace.title=Chercher et remplacer coser.ui.control.save=Sauvegarder -coser.ui.error.htmlmessage=Une erreur s'est produite\u2009\:\u2009%s +coser.ui.error.htmlmessage=Une erreur s'est produite \: %s coser.ui.error.title=Erreur globale coser.ui.mainframe.menu.configuration=Configuration coser.ui.mainframe.menu.file=Fichier @@ -42,38 +43,42 @@ coser.ui.mainframe.menu.window.selection=S\u00E9lection coser.ui.mainframe.menu.window.validation=Validation coser.ui.mainview.title=Contr\u00F4le et S\u00E9lection RSufi -coser.ui.project.captureFile=Fichier de captures\u2009\: +coser.ui.project.captureFile=Fichier de captures \: coser.ui.project.createProject=Cr\u00E9er le projet coser.ui.project.creationError=Erreur de cr\u00E9ation -coser.ui.project.customReferenceSpeciesFile=Nouveau fichier de r\u00E9f\u00E9rence\u2009\: -coser.ui.project.lengthFile=Fichiers des tailles\u2009\: +coser.ui.project.customReferenceSpeciesFile=Nouveau fichier de r\u00E9f\u00E9rence \: +coser.ui.project.lengthFile=Fichiers des tailles \: coser.ui.project.newProject=Nouveau projet coser.ui.project.openError=Erreur d'ouverture coser.ui.project.openProject=Ouvrir coser.ui.project.openProjectTitle=Ouvrir un projet existant -coser.ui.project.project=Projet\u2009\: -coser.ui.project.projectname=Nom du projet\u2009\: -coser.ui.project.stratesFile=Fichiers des strates\u2009\: -coser.ui.project.traitsFile=Fichiers des traits\u2009\: +coser.ui.project.project=Projet \: +coser.ui.project.projectname=Nom du projet \: +coser.ui.project.stratesFile=Fichiers des strates \: +coser.ui.project.traitsFile=Fichiers des traits \: coser.ui.project.useCustomReferenceSpeciesFile=Utiliser un autre fichier de r\u00E9f\u00E9rence -coser.ui.project.usedReferenceSpeciesFile=Fichier de r\u00E9f\u00E9rence utilis\u00E9\u2009\: +coser.ui.project.usedReferenceSpeciesFile=Fichier de r\u00E9f\u00E9rence utilis\u00E9 \: +coser.ui.result.availableDataTitle=R\u00E9sultats disponibles \: +coser.ui.result.extractDataButton=Extraire +coser.ui.result.extractDataLabel=Dossier d'extraction \: +coser.ui.result.extractDataTitle=Extraction des donn\u00E9es d'entr\u00E9e de RSufi coser.ui.selection.allSpecies=Toutes les esp\u00E8ces -coser.ui.selection.details.beginDate=Date de d\u00E9but\u2009\: -coser.ui.selection.details.endDate=Date de fin\u2009\: -coser.ui.selection.details.family=Filtrer par famille\u2009\: -coser.ui.selection.details.gender=Filtrer par genre\u2009\: -coser.ui.selection.details.name=Nom de la s\u00E9lection\u2009\: +coser.ui.selection.details.beginDate=Date de d\u00E9but \: +coser.ui.selection.details.endDate=Date de fin \: +coser.ui.selection.details.family=Filtrer par famille \: +coser.ui.selection.details.gender=Filtrer par genre \: +coser.ui.selection.details.name=Nom de la s\u00E9lection \: coser.ui.selection.details.otherComment=Autre commentaire coser.ui.selection.details.saveSelection=Sauvegarder la s\u00E9lection -coser.ui.selection.details.species=Esp\u00E8ces\u2009\: +coser.ui.selection.details.species=Esp\u00E8ces \: coser.ui.selection.details.technicalComment=Commentaire technique -coser.ui.selection.details.zone=Zone\u2009\: -coser.ui.selection.filter.density=Densit\u00E9\u2009\: +coser.ui.selection.details.zone=Zone \: +coser.ui.selection.filter.density=Densit\u00E9 \: coser.ui.selection.filter.filter=Filtrer -coser.ui.selection.filter.occurrence=Occurence\u2009\: +coser.ui.selection.filter.occurrence=Occurence \: coser.ui.selection.fusion.comment=Commentaire coser.ui.selection.fusion.description=Fusion de deux esp\u00E8ces. Veuillez renseigner un nouveau nom pr\u00E9sent dans le r\u00E9f\u00E9rentiel. -coser.ui.selection.fusion.name=Nouveau nom\u2009\: +coser.ui.selection.fusion.name=Nouveau nom \: coser.ui.selection.fusion.title=Fusion coser.ui.selection.fusionError=Erreur de fusion coser.ui.selection.invalidFusionName=Le nom de fusion choisit n'est pas pr\u00E9sent de le r\u00E9f\u00E9rentiel \! @@ -94,4 +99,4 @@ coser.ui.validators.newValidator=Nouveau validateur coser.ui.validators.title=Validateurs coser.ui.validators.valid=Valider -coser.ui.validators.validators=Validateurs\u2009\: +coser.ui.validators.validators=Validateurs \:
participants (1)
-
chatellier@users.labs.libre-entreprise.org