This is an automated email from the git hooks/post-receive script. New commit to branch feature/7739 in repository tutti. See https://gitlab.nuiton.org/codelutin/tutti.git commit 3a9e5d3604305539c34052817b269b4d985bfa97 Author: Tony CHEMIT <chemit@codelutin.com> Date: Wed Jul 13 12:53:10 2016 +0200 Début de migration des interfaces graphiques pour la synchronisation (See #7739) --- .../observe/ui/admin/save/SaveLocalUIHandler.java | 220 +++-- .../synchronize/ObsoleteEntityTableModel.java | 158 ---- .../synchronize/ObsoleteReferentialReference.java | 53 ++ ...oleteReferentialReferenceListCellRenderer.java} | 29 +- .../ui/admin/synchronize/SynchronizeModel.java | 129 ++- .../ui/admin/synchronize/SynchronizeUI.jaxx | 64 +- .../ui/admin/synchronize/SynchronizeUI.jcss | 15 +- .../ui/admin/synchronize/SynchronizeUIHandler.java | 969 ++++++--------------- .../observe-application-swing_en_GB.properties | 1 + .../observe-application-swing_es_ES.properties | 1 + .../observe-application-swing_fr_FR.properties | 1 + 11 files changed, 523 insertions(+), 1117 deletions(-) diff --git a/observe-application-swing/src/main/java/fr/ird/observe/ui/admin/save/SaveLocalUIHandler.java b/observe-application-swing/src/main/java/fr/ird/observe/ui/admin/save/SaveLocalUIHandler.java index 18219c1..2fd3ece 100644 --- a/observe-application-swing/src/main/java/fr/ird/observe/ui/admin/save/SaveLocalUIHandler.java +++ b/observe-application-swing/src/main/java/fr/ird/observe/ui/admin/save/SaveLocalUIHandler.java @@ -28,7 +28,6 @@ import fr.ird.observe.ui.UIHelper; import fr.ird.observe.ui.admin.AdminStep; import fr.ird.observe.ui.admin.AdminTabUIHandler; import fr.ird.observe.ui.admin.AdminUI; -import fr.ird.observe.ui.admin.synchronize.SynchronizeModel; import jaxx.runtime.swing.wizard.ext.WizardState; import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; @@ -37,7 +36,6 @@ import org.apache.commons.logging.LogFactory; import java.io.File; import java.io.FileOutputStream; import java.util.Date; -import java.util.List; import java.util.concurrent.Callable; import static org.nuiton.i18n.I18n.t; @@ -171,126 +169,126 @@ public class SaveLocalUIHandler extends AdminTabUIHandler { } - if (stepModel.containsStepForsave(AdminStep.SYNCHRONIZE)) { - - sendMessage("Sauvegarde du référentiel."); - saveReferentiel(); - } +// if (stepModel.containsStepForsave(AdminStep.SYNCHRONIZE)) { +// +// sendMessage("Sauvegarde du référentiel."); +// saveReferentiel(); +// } sendMessage(t("observe.message.synchro.operation.done", new Date())); return WizardState.SUCCESSED; } - public void saveReferentiel() throws Exception { - - List<String> ids; - - SynchronizeModel stepModel = getModel().getSynchronizeReferentielModel(); - - ObserveSwingDataSource referentielSource = stepModel.getTmpSource(); - if (referentielSource == null) { - - // pas de base temporaire - // on travaille directement sur la base central - referentielSource = stepModel.getCentralSource(); - } - ObserveSwingDataSource targetSource = stepModel.getSource(); - - //FIXME -// DiffState.DiffStateMap diff = stepModel.getDiff(); -// -// String txName = "saveReferentiel"; -// TopiaContext targetTx = beginTransaction(targetSource, txName); -// try { -// TopiaContext sourceTx = beginTransaction(referentielSource, txName); -// try { -// // ajout des nouvelles entites du referentiel -// -// ids = diff.get(DiffState.NEW); -// if (CollectionUtils.isNotEmpty(ids)) { -// for (String id : ids) { -// //FIXME on doit s'assurer de l'orde d'injection des entites -// TopiaEntity entity = sourceTx.findByTopiaId(id); -// String message = t("observe.synchro.add.object", id); -// sendMessage(message); -// sourceTx.replicateEntity(targetTx, entity); -// -// // On est obligé de commiter à chaque ajout sinon si -// // une autre entité à ajouter depend de celle là, on -// // ne la retrouve pas -// // -// commitTransaction(targetSource, targetTx, txName); -// if (log.isDebugEnabled()) { -// log.debug("add [" + id + "] : " + targetTx.findByTopiaId(id)); -// } -// } -// } -// -// // mis a jour des entites modifiees -// -// ids = diff.get(DiffState.MODIFIED); -// if (CollectionUtils.isNotEmpty(ids)) { -// -// for (String id : ids) { -// -// copyEntity(stepModel, sourceTx, targetTx, id); -// } -// } -// -// // mise à jour des données utilisateurs (remplacements d'objets obsolètes) -// -// for (SynchronizeUIHandler.ObsoleteRefReplaceAction action : stepModel.getReplaceActions()) { -// -// String message = t("observe.synchro.replaceObsolete.object", action.getObsoleteId()); -// sendMessage(message); +// public void saveReferentiel() throws Exception { // -// action.doAction(targetTx); -// } -// -// // si on ne commite pas ici, les modifications utilisateurs -// // sont perdues -// commitTransaction(targetSource, targetTx, txName); -// -// // suppression des entités obsoletes de la base source +// List<String> ids; // -// ids = diff.get(DiffState.REMOVED); -// if (CollectionUtils.isNotEmpty(ids)) { -// for (String id : ids) { -// String message = t("observe.synchro.remove.object", id); -// sendMessage(message); -// TopiaEntity entity = targetTx.findByTopiaId(id); -// getDAO(targetTx, entity).delete(entity); -// } -// } +// SynchronizeModel stepModel = getModel().getSynchronizeReferentielModel(); // -// Map<TopiaEntity, Long> versionsToUpdate = -// stepModel.getVersionsToUpdate(); +// ObserveSwingDataSource referentielSource = stepModel.getTmpSource(); +// if (referentielSource == null) { // -// if (MapUtils.isNotEmpty(versionsToUpdate)) { -// -// // on applique le patch sur les versions pour bien avoir -// // la bonne version de topiaversion et pas seulement un incrément -// // de 1 puisque ce champs est géré par hibernate et qu'il ne nous laisse -// // pas la possibilité de choisir la valeur qu'on veut attribuer au champs... -// patchTopiaVersions((TopiaContextImplementor) targetTx, versionsToUpdate); -// } -// -// commitTransaction(targetSource, targetTx, txName); -// } finally { -// try { -// rollbackTransaction(referentielSource, sourceTx, txName); -// } finally { -// closeTransaction(referentielSource, sourceTx, txName); -// } -// } -// } catch (Exception e) { -// rollbackTransaction(targetSource, targetTx, txName); -// throw e; -// } finally { -// closeTransaction(targetSource, targetTx, txName); +// // pas de base temporaire +// // on travaille directement sur la base central +// referentielSource = stepModel.getCentralSource(); // } - } +// ObserveSwingDataSource targetSource = stepModel.getSource(); +// +// //FIXME +//// DiffState.DiffStateMap diff = stepModel.getDiff(); +//// +//// String txName = "saveReferentiel"; +//// TopiaContext targetTx = beginTransaction(targetSource, txName); +//// try { +//// TopiaContext sourceTx = beginTransaction(referentielSource, txName); +//// try { +//// // ajout des nouvelles entites du referentiel +//// +//// ids = diff.get(DiffState.NEW); +//// if (CollectionUtils.isNotEmpty(ids)) { +//// for (String id : ids) { +//// //FIXME on doit s'assurer de l'orde d'injection des entites +//// TopiaEntity entity = sourceTx.findByTopiaId(id); +//// String message = t("observe.synchro.add.object", id); +//// sendMessage(message); +//// sourceTx.replicateEntity(targetTx, entity); +//// +//// // On est obligé de commiter à chaque ajout sinon si +//// // une autre entité à ajouter depend de celle là, on +//// // ne la retrouve pas +//// // +//// commitTransaction(targetSource, targetTx, txName); +//// if (log.isDebugEnabled()) { +//// log.debug("add [" + id + "] : " + targetTx.findByTopiaId(id)); +//// } +//// } +//// } +//// +//// // mis a jour des entites modifiees +//// +//// ids = diff.get(DiffState.MODIFIED); +//// if (CollectionUtils.isNotEmpty(ids)) { +//// +//// for (String id : ids) { +//// +//// copyEntity(stepModel, sourceTx, targetTx, id); +//// } +//// } +//// +//// // mise à jour des données utilisateurs (remplacements d'objets obsolètes) +//// +//// for (SynchronizeUIHandler.ObsoleteRefReplaceAction action : stepModel.getReplaceActions()) { +//// +//// String message = t("observe.synchro.replaceObsolete.object", action.getObsoleteId()); +//// sendMessage(message); +//// +//// action.doAction(targetTx); +//// } +//// +//// // si on ne commite pas ici, les modifications utilisateurs +//// // sont perdues +//// commitTransaction(targetSource, targetTx, txName); +//// +//// // suppression des entités obsoletes de la base source +//// +//// ids = diff.get(DiffState.REMOVED); +//// if (CollectionUtils.isNotEmpty(ids)) { +//// for (String id : ids) { +//// String message = t("observe.synchro.remove.object", id); +//// sendMessage(message); +//// TopiaEntity entity = targetTx.findByTopiaId(id); +//// getDAO(targetTx, entity).delete(entity); +//// } +//// } +//// +//// Map<TopiaEntity, Long> versionsToUpdate = +//// stepModel.getVersionsToUpdate(); +//// +//// if (MapUtils.isNotEmpty(versionsToUpdate)) { +//// +//// // on applique le patch sur les versions pour bien avoir +//// // la bonne version de topiaversion et pas seulement un incrément +//// // de 1 puisque ce champs est géré par hibernate et qu'il ne nous laisse +//// // pas la possibilité de choisir la valeur qu'on veut attribuer au champs... +//// patchTopiaVersions((TopiaContextImplementor) targetTx, versionsToUpdate); +//// } +//// +//// commitTransaction(targetSource, targetTx, txName); +//// } finally { +//// try { +//// rollbackTransaction(referentielSource, sourceTx, txName); +//// } finally { +//// closeTransaction(referentielSource, sourceTx, txName); +//// } +//// } +//// } catch (Exception e) { +//// rollbackTransaction(targetSource, targetTx, txName); +//// throw e; +//// } finally { +//// closeTransaction(targetSource, targetTx, txName); +//// } +// } //FIXME // protected <E extends TopiaEntity> void copyEntity(SynchronizeModel synchronizeModel, diff --git a/observe-application-swing/src/main/java/fr/ird/observe/ui/admin/synchronize/ObsoleteEntityTableModel.java b/observe-application-swing/src/main/java/fr/ird/observe/ui/admin/synchronize/ObsoleteEntityTableModel.java deleted file mode 100644 index 0d9fa05..0000000 --- a/observe-application-swing/src/main/java/fr/ird/observe/ui/admin/synchronize/ObsoleteEntityTableModel.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * #%L - * ObServe :: Swing - * %% - * Copyright (C) 2008 - 2010 IRD, Codelutin, Tony Chemit - * %% - * 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.ird.observe.ui.admin.synchronize; - -import fr.ird.observe.services.dto.AbstractObserveDto; -import org.nuiton.i18n.I18n; - -import javax.swing.table.AbstractTableModel; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * @author Tony Chemit - chemit@codelutin.com - * @since 1.0 - */ -public class ObsoleteEntityTableModel extends AbstractTableModel { - - protected static final String[] COLUMN_NAMES = { - I18n.n("observe.synchro.common.select"), - I18n.n("observe.synchro.common.reference") - }; - - protected static final Class<?>[] COLUMN_CLASSES = { - Boolean.class, - AbstractObserveDto.class - }; - - private static final long serialVersionUID = 1L; - -// protected List<TopiaEntityRef> refs; - - protected Set<Integer> selected; - - protected boolean selectAll; - - public ObsoleteEntityTableModel() { - selected = new HashSet<>(); - } - - @Override - public Class<?> getColumnClass(int columnIndex) { - return COLUMN_CLASSES[columnIndex]; - } - - @Override - public boolean isCellEditable(int rowIndex, int columnIndex) { - return columnIndex == 0; - } - - //FIXME -// public void initEntity(List<TopiaEntityRef> refs) { -// // on nettoye toujours le model lors de l'init d'une entité -// this.refs = refs; -// selected.clear(); -// // par defaut, on selectionne toutes les references -// setSelectAll(true); -// //fireTableDataChanged(); -// } - - public List<Integer> getSelectedIndex() { - return new ArrayList<>(selected); - } - - //FIXME -// public List<TopiaEntityRef> getSelectedRefs() { -// List<TopiaEntityRef> result = new ArrayList<TopiaEntityRef>(); -// if (!selected.isEmpty()) { -// for (Integer i : selected) { -// result.add(refs.get(i)); -// } -// } -// return result; -// } - - @Override - public int getRowCount() { - //FIXME -// return refs == null ? 0 : refs.size(); - return -1; - } - - @Override - public int getColumnCount() { - return COLUMN_CLASSES.length; - } - - @Override - public Object getValueAt(int rowIndex, int columnIndex) { - if (columnIndex == 0) { - return selected.contains(rowIndex); - } - if (columnIndex == 1) { - //FIXME -// return refs.get(rowIndex); - return null; - } - throw new IllegalStateException("can not get value for row " + - rowIndex + ", col " + columnIndex); - } - - @Override - public void setValueAt(Object aValue, int rowIndex, int columnIndex) { - if (columnIndex == 0) { - Boolean value = (Boolean) aValue; - if (value) { - selected.add(rowIndex); - if (selected.size() == getRowCount()) { - selectAll = true; - } - } else { - selected.remove(rowIndex); - if (selected.isEmpty()) { - selectAll = false; - } - } - fireTableCellUpdated(rowIndex, columnIndex); - } - - // no edit for others columns - } - - public boolean isSelectAll() { - return selectAll; - } - - public void setSelectAll(boolean selectAll) { - this.selectAll = selectAll; - if (selectAll) { - for (int i = 0, max = getRowCount(); i < max; i++) { - selected.add(i); - } - } else { - selected.clear(); - } - fireTableDataChanged(); - } -} diff --git a/observe-application-swing/src/main/java/fr/ird/observe/ui/admin/synchronize/ObsoleteReferentialReference.java b/observe-application-swing/src/main/java/fr/ird/observe/ui/admin/synchronize/ObsoleteReferentialReference.java new file mode 100644 index 0000000..d5e60c5 --- /dev/null +++ b/observe-application-swing/src/main/java/fr/ird/observe/ui/admin/synchronize/ObsoleteReferentialReference.java @@ -0,0 +1,53 @@ +package fr.ird.observe.ui.admin.synchronize; + +import fr.ird.observe.services.dto.referential.ReferentialReference; + +import java.util.Objects; + +/** + * Représente une référence obsolète à remplacer. + * + * Created on 13/07/16. + * + * @author Tony Chemit - chemit@codelutin.com + * @since 5.0 + */ +public class ObsoleteReferentialReference { + + private final String referentialName; + private final ReferentialReference referentialReference; + + public ObsoleteReferentialReference(String referentialName, ReferentialReference referentialReference) { + this.referentialName = referentialName; + this.referentialReference = referentialReference; + } + + public String getReferentialName() { + return referentialName; + } + + public ReferentialReference getReferentialReference() { + return referentialReference; + } + + public String getId() { + return referentialReference.getId(); + } + + public Class getType() { + return referentialReference.getType(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ObsoleteReferentialReference that = (ObsoleteReferentialReference) o; + return Objects.equals(getReferentialReference(), that.getReferentialReference()); + } + + @Override + public int hashCode() { + return Objects.hash(getReferentialReference()); + } +} diff --git a/observe-application-swing/src/main/java/fr/ird/observe/ui/admin/synchronize/ObsoleteEntityListCellRenderer.java b/observe-application-swing/src/main/java/fr/ird/observe/ui/admin/synchronize/ObsoleteReferentialReferenceListCellRenderer.java similarity index 66% rename from observe-application-swing/src/main/java/fr/ird/observe/ui/admin/synchronize/ObsoleteEntityListCellRenderer.java rename to observe-application-swing/src/main/java/fr/ird/observe/ui/admin/synchronize/ObsoleteReferentialReferenceListCellRenderer.java index 462d322..ff15ad6 100644 --- a/observe-application-swing/src/main/java/fr/ird/observe/ui/admin/synchronize/ObsoleteEntityListCellRenderer.java +++ b/observe-application-swing/src/main/java/fr/ird/observe/ui/admin/synchronize/ObsoleteReferentialReferenceListCellRenderer.java @@ -23,21 +23,19 @@ package fr.ird.observe.ui.admin.synchronize; import fr.ird.observe.ObserveSwingApplicationContext; import fr.ird.observe.ui.DecoratorService; -import fr.ird.observe.services.dto.AbstractObserveDto; import org.nuiton.decorator.Decorator; -import javax.swing.*; -import java.awt.*; -import java.util.List; -import java.util.Map; +import javax.swing.DefaultListCellRenderer; +import javax.swing.JList; +import java.awt.Component; import static org.nuiton.i18n.I18n.t; -public class ObsoleteEntityListCellRenderer extends DefaultListCellRenderer { +public class ObsoleteReferentialReferenceListCellRenderer extends DefaultListCellRenderer { private static final long serialVersionUID = 1L; - protected DecoratorService decoratorService; + protected transient DecoratorService decoratorService; public DecoratorService getDecoratorService() { if (decoratorService == null) { @@ -54,20 +52,15 @@ public class ObsoleteEntityListCellRenderer extends DefaultListCellRenderer { boolean isSelected, boolean cellHasFocus) { - Map.Entry<?, ?> entry = (Map.Entry<?, ?>) value; - AbstractObserveDto id = (AbstractObserveDto) entry.getKey(); - List<?> val = (List<?>) entry.getValue(); + ObsoleteReferentialReference referentialReference = (ObsoleteReferentialReference) value; DecoratorService service = getDecoratorService(); - String type = t(DecoratorService.getEntityLabel(id.getClass())); - Decorator<?> decorator = service.getDecoratorByType(id.getClass()); + Class referentialReferenceType = referentialReference.getReferentialReference().getType(); + String type = t(DecoratorService.getEntityLabel(referentialReferenceType)); + Decorator<?> decorator = service.getReferentialReferenceDecorator(referentialReferenceType); + + String text = type + " " + service.decorate(referentialReference); - String decorated = decorator.toString(id); - if (val.size() == 1) { - value = t("observe.synchro.message.obsolote.entity.ref.found", type, decorated); - } else { - value = t("observe.synchro.message.obsolote.entity.refs.found", type, decorated, val.size()); - } return super.getListCellRendererComponent( list, value, diff --git a/observe-application-swing/src/main/java/fr/ird/observe/ui/admin/synchronize/SynchronizeModel.java b/observe-application-swing/src/main/java/fr/ird/observe/ui/admin/synchronize/SynchronizeModel.java index a3eef7d..86a3de7 100644 --- a/observe-application-swing/src/main/java/fr/ird/observe/ui/admin/synchronize/SynchronizeModel.java +++ b/observe-application-swing/src/main/java/fr/ird/observe/ui/admin/synchronize/SynchronizeModel.java @@ -22,8 +22,16 @@ package fr.ird.observe.ui.admin.synchronize; import fr.ird.observe.db.ObserveSwingDataSource; +import fr.ird.observe.services.service.actions.synchro.UnidirectionalReferentialSynchronizeCallbackResults; +import fr.ird.observe.services.service.actions.synchro.UnidirectionalReferentialSynchronizeContext; +import fr.ird.observe.services.service.actions.synchro.UnidirectionalReferentialSynchronizeEngine; +import fr.ird.observe.services.service.actions.synchro.UnidirectionalReferentialSynchronizeResult; import fr.ird.observe.ui.admin.AdminActionModel; import fr.ird.observe.ui.admin.AdminStep; +import jaxx.runtime.swing.model.JaxxDefaultListModel; + +import javax.swing.DefaultListSelectionModel; +import java.util.List; /** * Le modèle de l'opération de synchronization de réferentiel. @@ -33,81 +41,25 @@ import fr.ird.observe.ui.admin.AdminStep; */ public class SynchronizeModel extends AdminActionModel { - /** le dictionnaire des références sur du référentiel */ - //FIXME -// protected SortedMap<TopiaEntity, List<TopiaEntityRef>> obsoleteRefs; - - /** le différentiel de référentiel */ - //FIXME -// protected DiffState.DiffStateMap diff; - - /** liste des actions utilisateurs */ - //FIXME -// protected List<SynchronizeUIHandler.ObsoleteRefReplaceAction> replaceActions; - - /** Universe of entities to update and with their topiaVersion to set. */ - //FIXME -// protected Map<TopiaEntity, Long> versionsToUpdate; - /** data source we want to synchronize. */ protected ObserveSwingDataSource source; /** data source which contains central referentiel. */ protected ObserveSwingDataSource centralSource; - /** temporary source where to make objectOperations. */ - protected ObserveSwingDataSource tmpSource; + /** Le resultat de la synchronisation des référentiels. */ + private UnidirectionalReferentialSynchronizeResult referentialSynchronizeResult; + private UnidirectionalReferentialSynchronizeEngine engine; + private UnidirectionalReferentialSynchronizeContext referentialSynchronizeContext; + private UnidirectionalReferentialSynchronizeCallbackResults referentialSynchronizeCallbackResults; + + private final JaxxDefaultListModel<ObsoleteReferentialReference> obsoleteReferences = new JaxxDefaultListModel<>(); + private final DefaultListSelectionModel obsoleteReferencesSelectionModel = new DefaultListSelectionModel(); public SynchronizeModel() { super(AdminStep.SYNCHRONIZE); } - //FIXME -// public SortedMap<TopiaEntity, List<TopiaEntityRef>> getObsoleteRefs() { -// return obsoleteRefs; -// } - - //FIXME -// public void setObsoleteRefs(SortedMap<TopiaEntity, List<TopiaEntityRef>> obsoleteRefs) { -// this.obsoleteRefs = obsoleteRefs; -// } - - //FIXME -// public DiffState.DiffStateMap getDiff() { -// return diff; -// } - - //FIXME -// public void setDiff(DiffState.DiffStateMap diff) { -// this.diff = diff; -// } - - //FIXME -// public List<SynchronizeUIHandler.ObsoleteRefReplaceAction> getReplaceActions() { -// if (replaceActions == null) { -// replaceActions = new ArrayList<SynchronizeUIHandler.ObsoleteRefReplaceAction>(); -// } -// return replaceActions; -// } - - //FIXME -// public void setReplaceActions(List<SynchronizeUIHandler.ObsoleteRefReplaceAction> replaceActions) { -// this.replaceActions = replaceActions; -// } - - //FIXME -// public Map<TopiaEntity, Long> getVersionsToUpdate() { -// if (versionsToUpdate == null) { -// versionsToUpdate = new HashMap<TopiaEntity, Long>(); -// } -// return versionsToUpdate; -// } - - //FIXME -// public void setVersionsToUpdate(Map<TopiaEntity, Long> versionsToUpdate) { -// this.versionsToUpdate = versionsToUpdate; -// } - public ObserveSwingDataSource getSource() { return source; } @@ -124,11 +76,52 @@ public class SynchronizeModel extends AdminActionModel { this.centralSource = centralSource; } - public ObserveSwingDataSource getTmpSource() { - return tmpSource; + public UnidirectionalReferentialSynchronizeResult getReferentialSynchronizeResult() { + return referentialSynchronizeResult; + } + + public void setReferentialSynchronizeResult(UnidirectionalReferentialSynchronizeResult referentialSynchronizeResult) { + this.referentialSynchronizeResult = referentialSynchronizeResult; + } + + public void setEngine(UnidirectionalReferentialSynchronizeEngine engine) { + this.engine = engine; + } + + public UnidirectionalReferentialSynchronizeEngine getEngine() { + return engine; + } + + public void setReferentialSynchronizeContext(UnidirectionalReferentialSynchronizeContext referentialSynchronizeContext) { + this.referentialSynchronizeContext = referentialSynchronizeContext; + } + + public UnidirectionalReferentialSynchronizeContext getReferentialSynchronizeContext() { + return referentialSynchronizeContext; + } + + public UnidirectionalReferentialSynchronizeCallbackResults getReferentialSynchronizeCallbackResults() { + return referentialSynchronizeCallbackResults; + } + + public void setReferentialSynchronizeCallbackResults(UnidirectionalReferentialSynchronizeCallbackResults referentialSynchronizeCallbackResults) { + this.referentialSynchronizeCallbackResults = referentialSynchronizeCallbackResults; + } + + public JaxxDefaultListModel<ObsoleteReferentialReference> getObsoleteReferences() { + return obsoleteReferences; + } + + public DefaultListSelectionModel getObsoleteReferencesSelectionModel() { + return obsoleteReferencesSelectionModel; + } + + public void destroy() { + obsoleteReferencesSelectionModel.clearSelection(); + obsoleteReferences.clear(); } - public void setTmpSource(ObserveSwingDataSource tmpSource) { - this.tmpSource = tmpSource; + public void setObsoleteReferences(List<ObsoleteReferentialReference> obsoleteReferences) { + this.obsoleteReferences.setAllElements(obsoleteReferences); } } diff --git a/observe-application-swing/src/main/java/fr/ird/observe/ui/admin/synchronize/SynchronizeUI.jaxx b/observe-application-swing/src/main/java/fr/ird/observe/ui/admin/synchronize/SynchronizeUI.jaxx index 557ca32..f0d7696 100644 --- a/observe-application-swing/src/main/java/fr/ird/observe/ui/admin/synchronize/SynchronizeUI.jaxx +++ b/observe-application-swing/src/main/java/fr/ird/observe/ui/admin/synchronize/SynchronizeUI.jaxx @@ -33,29 +33,14 @@ jaxx.runtime.swing.CardLayout2 jaxx.runtime.swing.wizard.ext.WizardState - java.beans.PropertyChangeEvent - - javax.swing.ListSelectionModel </import> <!-- un etat pour savoir quand on peut activer l'action apply --> <Boolean id='canApply' javaBean='false'/> - <Object id='obsoleteEntity' javaBean='null'/> - <Object id='safeEntity' javaBean='null'/> - <SynchronizeUIHandler id='handler' constructorParams='this'/> - <SynchronizeModel id='stepModel' - initializer='getModel().getSynchronizeReferentielModel()'/> - - <DefaultListModel id='errorsModel'/> - - <ObsoleteEntityTableModel id='obsoleteTableModel' - onTableChanged='getHandler().updateCanApply();'/> - - <DefaultListSelectionModel id='errorsSelectionModel' - onValueChanged='if (!event.getValueIsAdjusting() && !errorsSelectionModel.isSelectionEmpty()) { getHandler().updateSelectedObsoleteEntity(); }'/> + <SynchronizeModel id='stepModel' initializer='getModel().getSynchronizeReferentielModel()'/> <CardLayout2 id='safeRefsPanelLayout'/> @@ -71,11 +56,9 @@ public void initUI(AdminUI ui) { @Override public void destroy() { - errorsSelectionModel.clearSelection(); - errorsModel.removeAllElements(); - //FIXME - //obsoleteTableModel.initEntity(null); + stepModel.destroy(); super.destroy(); + safeRefsPanelLayout.reset(safeRefsPanel); } @Override @@ -83,18 +66,8 @@ public void updateState(WizardState newState) { getHandler().updateState(this, newState); } -@Override -public void propertyChange(PropertyChangeEvent evt) { - super.propertyChange(evt); - getHandler().onPropertyChanged(evt); -} - protected void apply() { - /*getHandler().resolvObsoleteReferences( - (TopiaEntity) obsoleteEntity, - obsoleteTableModel.getSelectedRefs(), - (TopiaEntity) getHandler().getSafeComboBox().getSelectedItem() - );*/ + getHandler().resolveObsoleteReference(); } ]]> @@ -105,8 +78,7 @@ protected void apply() { weighty='1'> <row> <cell> - <JButton id='startAction' - onActionPerformed="getHandler().doStartAction()"/> + <JButton id='startAction' onActionPerformed="getHandler().doStartAction()"/> </cell> </row> </Table> @@ -114,33 +86,12 @@ protected void apply() { <JPanel id='NEED_FIX_content'> <JScrollPane id='errorsPane' constraints='BorderLayout.NORTH'> - <JList id='errors'/> + <JList id='obsoleteReferencesList'/> </JScrollPane> <Table fill='both' constraints='BorderLayout.CENTER' border='{new TitledBorder(t("observe.synchro.obsolete.entity.fix"))}'> - <!-- rappel de l'entité obsolete a traiter --> - <row> - <cell> - <JLabel text='observe.synchro.obsolete.entity.to.change.label'/> - </cell> - <cell weightx='1' fill='both'> - <!-- rappel de l'entité obsolete a traiter --> - <JLabel id='obsoleteRefLabel'/> - </cell> - </row> - - <!-- table des références sur l'entité --> - <row> - <cell fill='both' columns='2' weighty='1'> - - <!-- table des références sur l'entité obsolète --> - <JScrollPane> - <JTable id='obsoleteRefs'/> - </JScrollPane> - </cell> - </row> <row> <cell> <JLabel text='observe.synchro.safe.entity.to.choose.label'/> @@ -159,8 +110,7 @@ protected void apply() { <row> <cell columns='2'> <!-- pour appliquer les remplacements de l'entité obsolète --> - <JButton id='applyAction' - onActionPerformed='apply()'/> + <JButton id='applyAction' onActionPerformed='getHandler().resolveObsoleteReference()'/> </cell> </row> </Table> diff --git a/observe-application-swing/src/main/java/fr/ird/observe/ui/admin/synchronize/SynchronizeUI.jcss b/observe-application-swing/src/main/java/fr/ird/observe/ui/admin/synchronize/SynchronizeUI.jcss index 274ee84..c8327d6 100644 --- a/observe-application-swing/src/main/java/fr/ird/observe/ui/admin/synchronize/SynchronizeUI.jcss +++ b/observe-application-swing/src/main/java/fr/ird/observe/ui/admin/synchronize/SynchronizeUI.jcss @@ -28,10 +28,6 @@ layout:{new BorderLayout()}; } -#errorsSelectionModel { - selectionMode:{ListSelectionModel.SINGLE_SELECTION}; -} - #startAction { actionIcon:"wizard-start"; } @@ -39,14 +35,11 @@ #errorsPane { columnHeaderView:{new JLabel(t("observe.synchro.obsolete.entities.list"))}; } -#errors { - model:{errorsModel}; - selectionModel:{errorsSelectionModel}; - cellRenderer:{new ObsoleteEntityListCellRenderer()}; -} -#obsoleteRefs { - model:{obsoleteTableModel}; +#obsoleteReferencesList { + model:{getStepModel().getObsoleteReferences()}; + selectionModel:{getStepModel().getObsoleteReferencesSelectionModel()}; + cellRenderer:{new ObsoleteReferentialReferenceListCellRenderer()}; } #safeRefsPanel { diff --git a/observe-application-swing/src/main/java/fr/ird/observe/ui/admin/synchronize/SynchronizeUIHandler.java b/observe-application-swing/src/main/java/fr/ird/observe/ui/admin/synchronize/SynchronizeUIHandler.java index f1d4df6..a1a22ec 100644 --- a/observe-application-swing/src/main/java/fr/ird/observe/ui/admin/synchronize/SynchronizeUIHandler.java +++ b/observe-application-swing/src/main/java/fr/ird/observe/ui/admin/synchronize/SynchronizeUIHandler.java @@ -21,43 +21,38 @@ */ package fr.ird.observe.ui.admin.synchronize; -import fr.ird.observe.ui.DecoratorService; +import com.google.common.collect.ImmutableSet; import fr.ird.observe.db.ObserveSwingDataSource; -import fr.ird.observe.services.dto.AbstractObserveDto; -import fr.ird.observe.ui.UIHelper; -import fr.ird.observe.ui.admin.AdminStep; -import fr.ird.observe.ui.admin.AdminTabUI; +import fr.ird.observe.services.dto.referential.ReferentialReference; +import fr.ird.observe.services.service.actions.synchro.UnidirectionalReferentialSynchronizeCallbackRequest; +import fr.ird.observe.services.service.actions.synchro.UnidirectionalReferentialSynchronizeCallbackRequests; +import fr.ird.observe.services.service.actions.synchro.UnidirectionalReferentialSynchronizeCallbackResults; +import fr.ird.observe.services.service.actions.synchro.UnidirectionalReferentialSynchronizeContext; +import fr.ird.observe.services.service.actions.synchro.UnidirectionalReferentialSynchronizeEngine; +import fr.ird.observe.services.service.actions.synchro.UnidirectionalReferentialSynchronizeResult; import fr.ird.observe.ui.admin.AdminTabUIHandler; import fr.ird.observe.ui.admin.AdminUI; +import fr.ird.observe.ui.util.decorator.ReferentialReferenceDecorator; import jaxx.runtime.swing.CardLayout2; -import jaxx.runtime.swing.editor.MyDefaultCellEditor; import jaxx.runtime.swing.editor.bean.BeanComboBox; +import jaxx.runtime.swing.model.JaxxDefaultListModel; import jaxx.runtime.swing.wizard.ext.WizardState; -import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.jxpath.JXPathContext; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.nuiton.decorator.Decorator; -import org.nuiton.decorator.JXPathDecorator; -import javax.swing.DefaultListModel; +import javax.swing.DefaultListSelectionModel; import javax.swing.JPanel; -import javax.swing.JTable; -import javax.swing.table.DefaultTableCellRenderer; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; +import javax.swing.ListSelectionModel; +import javax.swing.event.ListSelectionEvent; import java.util.ArrayList; -import java.util.Collections; +import java.util.Collection; import java.util.Date; -import java.util.Iterator; +import java.util.LinkedList; import java.util.List; -import java.util.Map; -import java.util.SortedMap; -import java.util.concurrent.Callable; +import java.util.stream.Collectors; -import static org.nuiton.i18n.I18n.n; import static org.nuiton.i18n.I18n.t; /** @@ -69,8 +64,7 @@ import static org.nuiton.i18n.I18n.t; public class SynchronizeUIHandler extends AdminTabUIHandler { /** Logger */ - private static final Log log = - LogFactory.getLog(SynchronizeUIHandler.class); + private static final Log log = LogFactory.getLog(SynchronizeUIHandler.class); public SynchronizeUIHandler(SynchronizeUI ui) { super(ui); @@ -90,743 +84,330 @@ public class SynchronizeUIHandler extends AdminTabUIHandler { super.initTabUI(ui, tabUI); if (log.isDebugEnabled()) { - log.debug(" specialized for [" + tabUI.getStep() + - "] for main ui " + ui.getClass().getName() + - "@" + System.identityHashCode(ui)); + log.debug(" specialized for [" + tabUI.getStep() + "] for main ui " + ui.getClass().getName() + "@" + System.identityHashCode(ui)); } - // tableau de la synchronisation des données des references obsoletes - - final JTable table = tabUI.getObsoleteRefs(); - table.setRowHeight(24); - UIHelper.fixTableColumnWidth(table, 0, 20); - - UIHelper.setI18nTableHeaderRenderer( - table, - n("observe.synchro.table.obsolete.entity.select"), - n("observe.synchro.table.obsolete.entity.select.tip"), - n("observe.synchro.table.obsolete.entity.label"), - n("observe.synchro.table.obsolete.entity.label.tip")); - - DefaultTableCellRenderer renderer = new DefaultTableCellRenderer(); - - tabUI.getStartAction().setText( - t("observe.action.synchro.launch.operation", - t(tabUI.getStep().getOperationLabel()))); - - tabUI.setContextValue(renderer, "defaultTableRenderer"); - - UIHelper.setTableColumnRenderer( - table, - 0, - UIHelper.newBooleanTableCellRenderer(renderer)); - //FIXME -// UIHelper.setTableColumnRenderer( -// table, 1, -// UIHelper.newDecorateTableCellRenderer(renderer, -// TopiaEntityRef.class) -// ); - UIHelper.setTableColumnEditor( - table, - 0, - MyDefaultCellEditor.newBooleanEditor(false)); - - // pour tout selectionner - deselectionner dans l'entete du tableau - table.getTableHeader().addMouseListener(new MouseAdapter() { - - @Override - public void mouseClicked(MouseEvent e) { - - int colIndex = - table.getTableHeader().columnAtPoint(e.getPoint()); - colIndex = table.convertColumnIndexToModel(colIndex); - if (colIndex == 0) { - ObsoleteEntityTableModel model = - (ObsoleteEntityTableModel) table.getModel(); - boolean oldValue = model.isSelectAll(); - // toggle selectAll - model.setSelectAll(!oldValue); - } - } - }); + tabUI.getStartAction().setText(t("observe.action.synchro.launch.operation", t(tabUI.getStep().getOperationLabel()))); + + DefaultListSelectionModel obsoleteReferenceSelectionModel = tabUI.getModel().getSynchronizeReferentielModel().getObsoleteReferencesSelectionModel(); + obsoleteReferenceSelectionModel.addListSelectionListener(this::updateSelectedObsoleteEntity); + obsoleteReferenceSelectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + } - @Override - public void updateState(AdminTabUI tabUI, WizardState newState) { - super.updateState(tabUI, newState); - if (newState == WizardState.NEED_FIX) { - //FIXME -// setObsoleteRefs(getStepModel().getObsoleteRefs()); - } + public void doStartAction() { + + addAdminWorker(getUi().getStartAction().getToolTipText(), this::doAction); + } - public void onPropertyChanged(PropertyChangeEvent evt) { - - String propertyName = evt.getPropertyName(); - - if ("refsResolved".equals(propertyName)) { - - updateSelectedObsoleteEntity(); - - } else if ("obsoleteResolved".equals(propertyName)) { - SynchronizeUI ui = (SynchronizeUI) evt.getSource(); - SynchronizeModel model = ui.getStepModel(); - AbstractObserveDto entity = (AbstractObserveDto) evt.getNewValue(); - DecoratorService decoratorService = getDecoratorService(); - String type = t(DecoratorService.getEntityLabel(entity.getClass())); - Decorator<?> decorator = - decoratorService.getDecoratorByType(entity.getClass()); - String label = t("observe.synchro.obsolete.entity.label", - type, decorator.toString(entity)); - sendMessage(t("observe.synchro.message.obsolete.entities.fixed", - label)); - - //FIXME -// if (model.getObsoleteRefs().isEmpty()) { -// sendMessage( -// t("observe.synchro.message.all.obsolete.entities.fixed") -// ); -// } else { -// //on recharge le dictionnaire des references -// setObsoleteRefs(model.getObsoleteRefs()); -// } + public WizardState doAction() throws Exception { + + if (log.isDebugEnabled()) { + log.debug(this); } + + SynchronizeModel stepModel = getStepModel(); + + // on cree les sources de données + + ObserveSwingDataSource source = model.getSafeLocalSource(false); + stepModel.setSource(source); + + ObserveSwingDataSource centralSource = model.getSafeCentralSource(false); + stepModel.setCentralSource(centralSource); + + openSource(source); + openSource(centralSource); + + // construction du différentiel + sendMessage(t("observe.message.synchro.build.diff", centralSource.getLabel())); + + + UnidirectionalReferentialSynchronizeEngine engine = new UnidirectionalReferentialSynchronizeEngine( + source.newUnidirectionalReferentialSynchronizeLocalService(), + centralSource.newUnidirectionalReferentialSynchronizeRemoteService()); + + stepModel.setEngine(engine); + + UnidirectionalReferentialSynchronizeContext unidirectionalReferentialSynchronizeContext = engine.prepareContext(); + + stepModel.setReferentialSynchronizeContext(unidirectionalReferentialSynchronizeContext); + + boolean needCallback = unidirectionalReferentialSynchronizeContext.isNeedCallback(); + + if (needCallback) { + + // il existe des références obsolètes à traiter + + sendMessage(t("observe.message.synchro.operation.needFix")); + + stepModel.setReferentialSynchronizeCallbackResults(new UnidirectionalReferentialSynchronizeCallbackResults()); + + // des références obsolètes ont été détectées, on prépare les interfaces graphiques + // avec les données à corriger + + List<ObsoleteReferentialReference> obsoleteReferentialReferences = new LinkedList<>(); + UnidirectionalReferentialSynchronizeCallbackRequests callbackRequests = stepModel.getReferentialSynchronizeContext().getCallbackRequests(); + + for (UnidirectionalReferentialSynchronizeCallbackRequest<?> callbackRequest : callbackRequests) { + + String referentialName = callbackRequest.getReferentialName(); + + obsoleteReferentialReferences.addAll( + callbackRequest.getReferentialsToReplace().stream() + .map(referentialReference -> new ObsoleteReferentialReference(referentialName, referentialReference)) + .collect(Collectors.toList())); + + } + + stepModel.setObsoleteReferences(obsoleteReferentialReferences); + + return WizardState.NEED_FIX; + + } + + // pas de reference obsolete à traiter + + // on termine le traitement + beforeSuccess(); + + return WizardState.SUCCESSED; + } - //FIXME -// public void setObsoleteRefs(SortedMap<AbstractObserveDto, List<TopiaEntityRef>> refs) { -// SynchronizeUI ui = getUi(); -// ui.errorsSelectionModel.clearSelection(); -// ui.errorsModel.removeAllElements(); -// for (Object entry : refs.entrySet()) { -// ui.errorsModel.addElement(entry); -// } -// ui.setCanApply(false); -// ui.errors.setSelectedIndex(0); -// } + public void resolveObsoleteReference() { - @SuppressWarnings({"unchecked"}) - public void updateSelectedObsoleteEntity() { - SynchronizeUI ui = getUi(); + ObsoleteReferentialReference obsoleteRef = (ObsoleteReferentialReference) getUi().getObsoleteReferencesList().getSelectedValue(); - int row = ui.errorsSelectionModel.getMinSelectionIndex(); + BeanComboBox<?> safeComboBox = getSafeComboBox(); + ReferentialReference safeRef = (ReferentialReference) safeComboBox.getSelectedItem(); + + UnidirectionalReferentialSynchronizeCallbackResults referentialSynchronizeCallbackResults = getStepModel().getReferentialSynchronizeCallbackResults(); + referentialSynchronizeCallbackResults.addCallbackResult(obsoleteRef.getReferentialName(), obsoleteRef.getId(), safeRef.getId()); + + // On supprime le référentiel corrigé de la liste des référentiels à corriger + + getStepModel().getObsoleteReferences().removeElement(obsoleteRef); + + // S'il ne reste plus de référentiel à corriger, on peut terminer le traitement + + if (getStepModel().getObsoleteReferences().isEmpty()) { + + beforeSuccess(); + + updateState(getUi(), WizardState.SUCCESSED); + + } - DefaultListModel errorsModel = ui.errorsModel; - //FIXME -// if (row == -1 || errorsModel.getElementAt(row) == null || -// ((Map.Entry<TopiaEntity, List<TopiaEntityRef>>) -// errorsModel.getElementAt(row)).getValue().isEmpty()) { -// // entree supprimee -// -// ui.obsoleteRefLabel.setText(""); -// ui.setObsoleteEntity(null); -// ui.obsoleteTableModel.initEntity(Collections.<TopiaEntityRef>emptyList()); -// } else { -// -// Map.Entry<TopiaEntity, List<TopiaEntityRef>> entry = -// (Map.Entry<TopiaEntity, List<TopiaEntityRef>>) -// errorsModel.getElementAt(row); -// TopiaEntity entity = entry.getKey(); -// ui.setObsoleteEntity(entity); -// -// List<TopiaEntityRef> refs = entry.getValue(); -// -// DecoratorService decoratorService = getDecoratorService(); -// String type = DecoratorService.getEntityLabel(entity.getClass()); -// Decorator<?> decorator = -// decoratorService.getDecoratorByType(entity.getClass()); -// String label = t("observe.synchro.obsolete.entity.label", -// type, -// decorator.toString(entity)); -// ui.obsoleteRefLabel.setText(label); -// -// changeSafeRefsComboBox(entry); -// -// ui.obsoleteTableModel.initEntity(refs); -// } } - public void updateCanApply() { + private void updateSelectedObsoleteEntity(ListSelectionEvent e) { + + if (e.getValueIsAdjusting()) { + return; + } + SynchronizeUI ui = getUi(); - ui.setCanApply(!ui.obsoleteTableModel.getSelectedIndex().isEmpty() && - getSafeComboBox() != null && - getSafeComboBox().getSelectedItem() != null); + int row = getStepModel().getObsoleteReferencesSelectionModel().getMinSelectionIndex(); + + JaxxDefaultListModel<ObsoleteReferentialReference> obsoleteReferences = getStepModel().getObsoleteReferences(); + + if (row != -1 && obsoleteReferences.getElementAt(row) != null) { + + ObsoleteReferentialReference referentialReference = obsoleteReferences.getElementAt(row); + + String key = referentialReference.getReferentialName(); + CardLayout2 safeRefsPanelLayout = ui.getSafeRefsPanelLayout(); + JPanel safeRefsPanel = ui.getSafeRefsPanel(); + if (safeRefsPanelLayout.contains(key)) { + + // la liste déroulante existe deja pour ce type + BeanComboBox<?> list = (BeanComboBox<?>) safeRefsPanelLayout.getComponent(safeRefsPanel, key); + + if (!getSafeComboBox().equals(list)) { + + // on l'affiche + safeRefsPanelLayout.show(safeRefsPanel, key); + + } + + } else { + + ReferentialReferenceDecorator decorator = getDecoratorService().getReferentialReferenceDecorator(referentialReference.getType()); + + UnidirectionalReferentialSynchronizeCallbackRequest<?> callbackRequest = getStepModel().getReferentialSynchronizeContext().getCallbackRequests().getCallbackRequest(key); + ImmutableSet<? extends ReferentialReference<?>> availableReferentials = callbackRequest.getAvailableReferentials(); + + List<ReferentialReference> data = new ArrayList<>(availableReferentials); + + // la liste n'existe pas encore + BeanComboBox<ReferentialReference> box = new BeanComboBox<>(ui); + box.setBean(this); + box.setProperty("safeEntity"); + box.setShowReset(true); + box.addPropertyChangeListener("selectedItem", evt -> updateCanApply()); + safeRefsPanel.add(box, key); + box.init(decorator, data); + + safeRefsPanelLayout.show(safeRefsPanel, key); + + } + + } } - public BeanComboBox<?> getSafeComboBox() { + private BeanComboBox<?> getSafeComboBox() { SynchronizeUI ui = getUi(); - JPanel panel = ui.getSafeRefsPanel(); - return (BeanComboBox<?>) - ui.getSafeRefsPanelLayout().getVisibleComponent(panel); + return (BeanComboBox<?>) ui.getSafeRefsPanelLayout().getVisibleComponent(panel); } - @SuppressWarnings({"unchecked"}) - //FIXME -// public void changeSafeRefsComboBox(Map.Entry<TopiaEntity, List<TopiaEntityRef>> entry) { -// SynchronizeUI ui = getUi(); + private void updateCanApply() { + SynchronizeUI ui = getUi(); + BeanComboBox<?> safeComboBox = getSafeComboBox(); + ui.setCanApply(safeComboBox != null && safeComboBox.getSelectedItem() != null); + } -// TopiaEntity entity = entry.getKey(); -// -// ObserveEntityEnum constant = ObserveEntityEnum.valueOf(entity); -// String key = constant.getContract().getName(); -// CardLayout2 safeRefsPanelLayout = ui.getSafeRefsPanelLayout(); -// JPanel safeRefsPanel = ui.getSafeRefsPanel(); -// if (safeRefsPanelLayout.contains(key)) { -// -// // la liste déroulante existe deja pour ce type -// BeanComboBox<?> list = (BeanComboBox<?>) -// safeRefsPanelLayout.getComponent(safeRefsPanel, key); -// -// if (!getSafeComboBox().equals(list)) { -// // on l'affiche -// safeRefsPanelLayout.show(safeRefsPanel, key); -// } -// return; -// } -// -// Decorator<?> decorator = -// getDecoratorService().getDecoratorByType(entity.getClass()); -// -// List<?> data = getEntityListFromSynchroDB(entity.getClass(), true); -// -// // la liste n'existe pas encore -// BeanComboBox<?> box = new BeanComboBox(ui); -// box.setBean(this); -// box.setProperty("safeEntity"); -// box.setShowReset(true); -// box.addPropertyChangeListener( -// "selectedItem", -// new PropertyChangeListener() { -// @Override -// public void propertyChange(PropertyChangeEvent evt) { -// updateCanApply(); -// } -// }); -// safeRefsPanel.add(box, key); -// box.init((JXPathDecorator) decorator, (List) data); -// -// safeRefsPanelLayout.show(safeRefsPanel, key); -// } + private void beforeSuccess() { - public void doStartAction() { + SynchronizeModel stepModel = getStepModel(); + + UnidirectionalReferentialSynchronizeEngine engine = stepModel.getEngine(); + + UnidirectionalReferentialSynchronizeContext referentialSynchronizeContext = stepModel.getReferentialSynchronizeContext(); + + UnidirectionalReferentialSynchronizeCallbackResults referentialSynchronizeCallbackResults = stepModel.getReferentialSynchronizeCallbackResults(); + + UnidirectionalReferentialSynchronizeResult referentialSynchronizeResult = engine.finish(referentialSynchronizeContext, referentialSynchronizeCallbackResults); + stepModel.setReferentialSynchronizeResult(referentialSynchronizeResult); + + if (referentialSynchronizeResult.isEmpty()) { + + sendMessage(t("observe.message.synchro.ref.is.updtodate")); + + } else { + + for (String referentialName : referentialSynchronizeResult.getReferentialNames()) { + + Collection<String> referentialAdded = referentialSynchronizeResult.getReferentialAdded(referentialName); + if (CollectionUtils.isNotEmpty(referentialAdded)) { - addAdminWorker( - getUi().getStartAction().getToolTipText(), - new Callable<WizardState>() { - @Override - public WizardState call() throws Exception { - return doAction(); + sendMessage(t("observe.message.synchro.referentiel.was.added", referentialAdded.size())); + for (String id : referentialAdded) { + sendMessage(" - " + id); } + } - ); - } - public WizardState doAction() throws Exception { + Collection<String> referentialUpdated = referentialSynchronizeResult.getReferentialUpdated(referentialName); + if (CollectionUtils.isNotEmpty(referentialUpdated)) { - if (log.isDebugEnabled()) { - log.debug(this); - } - - // on cree les sources de données + sendMessage(t("observe.message.synchro.referentiel.was.modified", referentialUpdated.size())); + for (String id : referentialUpdated) { + sendMessage(" - " + id); + } - ObserveSwingDataSource source = model.getSafeLocalSource(false); - getStepModel().setSource(source); - ObserveSwingDataSource centralSource = model.getSafeCentralSource(false); - getStepModel().setCentralSource(centralSource); + } - openSource(source); - openSource(centralSource); + Collection<String> referentialRemoved = referentialSynchronizeResult.getReferentialRemoved(referentialName); + if (CollectionUtils.isNotEmpty(referentialRemoved)) { - // construction du différentiel - sendMessage(t("observe.message.synchro.build.diff", - centralSource.getLabel())); - - //FIXME -// DiffState.DiffStateMap diff = -// getDataService().buildReferentielDifferentiel(centralSource, -// source, -// this -// ); -// getStepModel().setDiff(diff); -// -// if (diff.isEmpty() || -// CollectionUtils.isEmpty(diff.get(DiffState.REMOVED))) { -// -// // aucune modification du référentiel -// // ou aucune entité obsolète -// // aucune intervention requise -// -// reportSuccess(); -// -// // pas de reference obsolete a traiter -// return WizardState.SUCCESSED; -// } -// -// // detection des entités obsoletes -// -// SortedMap<TopiaEntity, List<TopiaEntityRef>> obsoleteRefs = -// detectObsoleteEntities(); -// getStepModel().setObsoleteRefs(obsoleteRefs); -// -// if (obsoleteRefs == null || obsoleteRefs.isEmpty()) { -// -// reportSuccess(); -// -// // pas de reference obsolete a traiter -// return WizardState.SUCCESSED; -// } -// -// // il existe des entités obsolètes utilisées dans la base locale -// -// // on conserve la liste complète des entités qui utilisent des -// // référentiels obsolètes (on va les recopier plus tard) -// -// List<TopiaEntity> obsoleteEntities = -// new ArrayList<TopiaEntity>(obsoleteRefs.keySet()); -// -// // on filtre les entités obsolètes directes (les seuls que l'utilisateur -// // peut changer) -// -// removeUndirectObsoleteRefs(); -// -// if (obsoleteRefs.isEmpty()) { -// -// reportSuccess(); -// -// // pas de reference obsolete directe a traiter -// return WizardState.SUCCESSED; -// } -// -// // dernier cas (le seul demandant une intervention humaine) -// // il y a des références obsolètes directes sur des données observers -// -// // on mets en place une base temporaire pour effectuer les corrections -// -// sendMessage( -// t("observe.message.synchro.create.temporary.db.to.resolve.obsoletes")); -// -// DataSource tmpSource = model.getSafeTmpSource(false); -// getStepModel().setTmpSource(tmpSource); -// openSource(tmpSource); -// -// // duplication du referentiel à jour vers la base temporaire -// replicateReferentiel(centralSource, tmpSource); -// -// // duplication des objets de la base locale qui utilisent les données obsolètes -// replicateObsoletesEntities(obsoleteEntities, source, tmpSource); -// -// // on injecte toutes les donnes utilisateurs dans la base de synchro -// // car on va ensuite remplacer -// replicateData(source, tmpSource); -// -// // la synchronisation demande des modification de la base locale -// // on enregistre cette action aupres de l'action de sauvegarde -// model.getSaveLocalModel().addStepForSave(AdminStep.SYNCHRONIZE); -// -// sendMessage(t("observe.message.synchro.operation.needFix")); + sendMessage(t("observe.message.synchro.referentiel.was.removed", referentialRemoved.size())); + for (String id : referentialRemoved) { + sendMessage(" - " + id); + } - return WizardState.NEED_FIX; - } + } - public void reportSuccess() { - boolean needSave = false; + Collection<Pair<String, String>> referentialReplaced = referentialSynchronizeResult.getReferentialReplaced(referentialName); + if (CollectionUtils.isNotEmpty(referentialReplaced)) { -// DiffState.DiffStateMap diff = getStepModel().getDiff(); -// -// List<String> ids = diff.get(DiffState.REMOVED); -// if (!CollectionUtils.isEmpty(ids)) { -// // des doonées ont ete supprimées du referentiel distant -// needSave = true; -// sendMessage( -// t("observe.message.synchro.referentiel.was.removed", -// ids.size())); -// for (String id : ids) { -// sendMessage(" - " + id); -// } -// } -// -// ids = diff.get(DiffState.NEW); -// if (!CollectionUtils.isEmpty(ids)) { -// // des données ont ete ajoutees au referentiel distant -// needSave = true; -// sendMessage(t("observe.message.synchro.referentiel.was.added", -// ids.size())); -// for (String id : ids) { -// sendMessage(" - " + id); -// } -// } -// -// ids = diff.get(DiffState.MODIFIED); -// if (!CollectionUtils.isEmpty(ids)) { -// // des doonées ont ete modifiees dans le referentiel distant -// needSave = true; -// sendMessage( -// t("observe.message.synchro.referentiel.was.modified", -// ids.size())); -// for (String id : ids) { -// sendMessage(" - " + id); -// } -// } -// -// if (needSave) { -// // des données doivent être transférer vers la base locale -// model.getSaveLocalModel().addStepForSave( -// AdminStep.SYNCHRONIZE); -// sendMessage(t("observe.message.synchro.local.modification")); -// sendMessage( -// t("observe.message.synchro.no.referentiel.conflict")); -// -// } else { -// sendMessage(t("observe.message.synchro.ref.is.updtodate")); -// } + sendMessage(t("observe.message.synchro.referentiel.was.replaced", referentialReplaced.size())); + for (Pair<String, String> ids : referentialReplaced) { + sendMessage(" - " + ids.getLeft() + " → " + ids.getRight()); + } - sendMessage( - t("observe.message.synchro.operation.done", new Date())); - } + } - //FIXME -// protected SortedMap<TopiaEntity, List<TopiaEntityRef>> detectObsoleteEntities() throws Exception { -// -// DiffState.DiffStateMap diff = getStepModel().getDiff(); -// -// DataSource source = getStepModel().getSource(); -// -// // detection des entites obsoletes -// -// List<String> removedList = diff.get(DiffState.REMOVED); -// -// String[] ids = removedList.toArray(new String[removedList.size()]); -// -// TopiaContext tx = beginTransaction(source, "detectObsoleteRefs"); -// -// // detection des entites obsoletes -// -// try { -// // des suppressions ont ete detectees, on doit retrouver -// // dans la base locale les entites utilisant ces entites -// // obsoletes -// List<TripSeine> marees = ObserveDAOHelper.getTripSeineDAO(tx).findAll(); -// -// ObserveEntityEnum[] contracts = ObserveDAOHelper.getContracts(); -// -// SortedMap<TopiaEntity, List<TopiaEntityRef>> result; -// -// result = TopiaEntityHelper.detectReferences(contracts, ids, marees); -// return result; -// } finally { -// closeTransaction(source, tx, "detectObsoleteRefs"); -// } -// } + } - //FIXME -// protected void removeUndirectObsoleteRefs() { -// -// Iterator<Map.Entry<TopiaEntity, List<TopiaEntityRef>>> itr; -// -// SortedMap<TopiaEntity, List<TopiaEntityRef>> obsoleteRefs = -// getStepModel().getObsoleteRefs(); -// -// for (itr = obsoleteRefs.entrySet().iterator(); itr.hasNext(); ) { -// Map.Entry<TopiaEntity, List<TopiaEntityRef>> entry = itr.next(); -// for (Iterator<TopiaEntityRef> itrRef = -// entry.getValue().iterator(); itrRef.hasNext(); ) { -// TopiaEntityRef ref = itrRef.next(); -// TopiaEntity refInvoker = ref.getInvoker(); -// if (refInvoker == null) { -// // on est sur un program -// // et on ne va pas resoudre un program obsolete -// itrRef.remove(); -// if (log.isTraceEnabled()) { -// log.trace("remove ref " + ref); -// } -// continue; -// } -// -// ObserveEntityEnum refConstant = -// ObserveEntityEnum.valueOf(refInvoker); -// if (!Entities.DATA_ENTITIES_LIST.contains( -// refConstant)) { -// // on peut supprimer cette référence -// itrRef.remove(); -// if (log.isTraceEnabled()) { -// log.trace("remove ref " + ref); -// } -// } -// } -// if (entry.getValue().isEmpty()) { -// // on peut supprimer cette entité obsolete car plus de -// // references dessus -// itr.remove(); -// if (log.isDebugEnabled()) { -// log.debug("remove obsolete entity (not direct " + -// "referentiel) : " + -// entry.getKey().getTopiaId()); -// } -// } -// } -// } + } - //FIXME -// protected void replicateObsoletesEntities( -// List<TopiaEntity> obsoleteEntities, -// DataSource source, -// DataSource tmpSource) throws Exception { -// -// String txName = "replicateObsoletesEntities"; -// TopiaContext sourceCtxt = beginTransaction(source, txName); -// try { -// TopiaContext tmpCtxt = beginTransaction(tmpSource, txName); -// try { -// for (ObserveEntityEnum constant : Entities.REFERENCE_ENTITIES) { -// Class<? extends TopiaEntity> contractClass = -// constant.getContract(); -// List<TopiaEntity> toReplicate = new ArrayList<TopiaEntity>(); -// for (Iterator<TopiaEntity> itr = -// obsoleteEntities.iterator(); itr.hasNext(); ) { -// TopiaEntity e = itr.next(); -// if (contractClass.isAssignableFrom(e.getClass())) { -// if (log.isDebugEnabled()) { -// log.debug("obsolete to inject : " + -// e.getTopiaId()); -// } -// itr.remove(); -// -// // cet objet doit etre replique -// toReplicate.add(e); -// -// sendMessage(t("observe.message.synchro.obsolete.data.to.duplicate", e)); -// } -// } -// if (toReplicate.isEmpty()) { -// if (log.isDebugEnabled()) { -// log.debug("no obsolete entity " + constant); -// } -// continue; -// } -// -// if (log.isDebugEnabled()) { -// log.debug("inject obsolete entity " + constant + " (" + -// toReplicate.size() + ")"); -// } -// sourceCtxt.replicateEntities(tmpCtxt, toReplicate); -// commitTransaction(tmpSource, tmpCtxt, txName); -// } -// -// } finally { -// closeTransaction(tmpSource, tmpCtxt, txName); -// } -// } finally { -// closeTransaction(source, sourceCtxt, txName); -// } -// -// if (!obsoleteEntities.isEmpty()) { -// throw new IllegalStateException( -// "there is still obsolete entities to inject in " + -// "synchro db : " + obsoleteEntities); -// } -// } + sendMessage(t("observe.message.synchro.operation.done", new Date())); - public List<?> getEntityListFromSynchroDB(Class<?> type, - boolean removeObsoletes) { - - //FIXME -// DataSource tmpSource = getStepModel().getTmpSource(); -// List<?> list; -// try { -// ObserveEntityEnum constant = ObserveEntityEnum.valueOf(type); -// list = getDataService().getList(tmpSource, constant.getContract()); -// } catch (DataSourceException ex) { -// //TODO should do better... -// if (log.isErrorEnabled()) { -// log.error(ex); -// } -// list = new ArrayList(); -// } -// if (removeObsoletes) { -// -// DiffState.DiffStateMap diff = getStepModel().getDiff(); -// -// List<String> removedList = diff.get(DiffState.REMOVED); -// for (Iterator<?> itr = list.iterator(); itr.hasNext(); ) { -// TopiaEntity e = (TopiaEntity) itr.next(); -// if (removedList.contains(e.getTopiaId())) { -// // c'est une entite qui est obsolete, l'utilisateur -// // ne peut pas l'utiliser -// itr.remove(); -// } -// } -// } -// return list; - return null; } - //FIXME -// public void resolvObsoleteReferences(TopiaEntity obsoleteRef, -// List<TopiaEntityRef> refs, -// TopiaEntity safeRef) { -// -// ObsoleteRefReplaceAction action = new ObsoleteRefReplaceAction( -// obsoleteRef.getTopiaId(), -// safeRef.getTopiaId(), -// refs); -// -// SynchronizeModel stepModel = getStepModel(); -// stepModel.getReplaceActions().add(action); -// -// SortedMap<TopiaEntity, List<TopiaEntityRef>> obsoleteRefs = -// stepModel.getObsoleteRefs(); -// -// List<TopiaEntityRef> globalRefs = obsoleteRefs.get(obsoleteRef); -// -// DataSource tmpSource = stepModel.getTmpSource(); -// -// String txName = "resolvObsoleteReferences"; -// TopiaContext tx = null; -// try { -// tx = beginTransaction(tmpSource, txName); -// -// action.doAction(tx); -// -// commitTransaction(tmpSource, tx, txName); -// -// // on supprime toutes les références traitées -// globalRefs.removeAll(refs); + +// public void changeSafeRefsComboBox(ObsoleteReferentialReference referentialReference) { // -// ui.firePropertyChange("refsResolved", null, obsoleteRef); +// SynchronizeUI ui = getUi(); // -// if (globalRefs.isEmpty()) { +// String key = referentialReference.getReferentialName(); +// CardLayout2 safeRefsPanelLayout = ui.getSafeRefsPanelLayout(); +// JPanel safeRefsPanel = ui.getSafeRefsPanel(); +// if (safeRefsPanelLayout.contains(key)) { // -// // la reference obsolete n'est pas utilisee dans aucune donnee +// // la liste déroulante existe deja pour ce type +// BeanComboBox<?> list = (BeanComboBox<?>) safeRefsPanelLayout.getComponent(safeRefsPanel, key); // -// obsoleteRefs.remove(obsoleteRef); +// if (!getSafeComboBox().equals(list)) { // -// if (log.isDebugEnabled()) { -// log.debug("remove resolved obsolete property from temp " + -// "db : " + obsoleteRef.getTopiaId()); -// } +// // on l'affiche +// safeRefsPanelLayout.show(safeRefsPanel, key); // -// obsoleteRef = tx.findByTopiaId(obsoleteRef.getTopiaId()); +// } // -// // on peut la supprimer de la base temporaire -// getDAO(tx, obsoleteRef).delete(obsoleteRef); +// } else { // -// ui.firePropertyChange("obsoleteResolved", null, obsoleteRef); -// } +// ReferentialReferenceDecorator decorator = getDecoratorService().getReferentialReferenceDecorator(referentialReference.getReferentialReference().getType()); // -// commitTransaction(tmpSource, tx, txName); +// UnidirectionalReferentialSynchronizeCallbackRequest<?> callbackRequest = getStepModel().getReferentialSynchronizeContext().getCallbackRequests().getCallbackRequest(key); +// ImmutableSet<? extends ReferentialReference<?>> availableReferentials = callbackRequest.getAvailableReferentials(); // -// if (obsoleteRefs.isEmpty()) { +// List<ReferentialReference> data = new ArrayList<>(availableReferentials); // -// sendMessage( -// t("observe.message.synchro.operation.done", new Date())); +// // la liste n'existe pas encore +// BeanComboBox<ReferentialReference> box = new BeanComboBox<>(ui); +// box.setBean(this); +// box.setProperty("safeEntity"); +// box.setShowReset(true); +// box.addPropertyChangeListener("selectedItem", evt -> updateCanApply()); +// safeRefsPanel.add(box, key); +// box.init(decorator, data); // -// // plus de references obsoletes a resoudre, l'action est -// // terminée -// model.setStepState(WizardState.SUCCESSED); -// } +// safeRefsPanelLayout.show(safeRefsPanel, key); // -// } catch (Exception e) { -// model.setStepState(onError(e)); -// } finally { -// try { -// if (tx != null) { -// closeTransaction(tmpSource, tx, txName); -// } -// } catch (DataSourceException e) { -// model.setStepState(onError(e)); -// } // } // } - //FIXME -// public class ObsoleteRefReplaceAction { +// @Override +// public void updateState(AdminTabUI tabUI, WizardState newState) { // -// /** l'id de l'entité à remplacer */ -// String obsoleteId; -// -// /** l'id de l'entité de remplacement */ -// String safeId; -// -// /** la liste des références à traiter */ -// TopiaEntityRef[] refs; -// -// ObsoleteRefReplaceAction(String obsoleteId, -// String safeId, -// List<TopiaEntityRef> refs) { -// this.obsoleteId = obsoleteId; -// this.refs = refs.toArray(new TopiaEntityRef[refs.size()]); -// this.safeId = safeId; -// } -// -// public void doAction(TopiaContext tx) throws TopiaException { -// -// if (log.isInfoEnabled()) { -// log.info("load obsolete object " + obsoleteId); -// } -// -// if (log.isInfoEnabled()) { -// log.info("load safe object " + safeId); -// } -// -// TopiaEntity safeRef = tx.findByTopiaId(safeId); -// -// // on remplace les references -// for (TopiaEntityRef ref : refs) { -// TopiaEntity invoker = ref.getInvoker(); -// -// if (invoker == null) { -// throw new NullPointerException( -// "can not have a null invoker in " + ref); -// } -// -// if (log.isInfoEnabled()) { -// log.info("load invoker object " + invoker.getTopiaId()); -// } -// -// invoker = tx.findByTopiaId(invoker.getTopiaId()); -// -// // switch entity -// -// String path = ref.getInvokerProperty(); -// -// JXPathContext jxcontext = JXPathContext.newContext(invoker); +// super.updateState(tabUI, newState); +// SynchronizeModel stepModel = getStepModel(); // -// TopiaEntity oldValue = (TopiaEntity) jxcontext.getValue(path); +// if (WizardState.NEED_FIX == newState) { // -// if (log.isDebugEnabled()) { -// log.debug("property to switch " + path + " old : " + -// oldValue); -// } +// // des références obsolètes ont été détectées, on prépare les interfaces graphiques +// // avec les données à corriger // -// if (log.isInfoEnabled()) { -// log.info("change path : " + path); -// log.info("old value : " + oldValue.getTopiaId()); -// } +// List<ObsoleteReferentialReference> obsoleteReferentialReferences = new LinkedList<>(); +// UnidirectionalReferentialSynchronizeCallbackRequests callbackRequests = stepModel.getReferentialSynchronizeContext().getCallbackRequests(); // -// jxcontext.setValue(path, safeRef); +// for (UnidirectionalReferentialSynchronizeCallbackRequest<?> callbackRequest : callbackRequests) { // -// TopiaEntity newValue = (TopiaEntity) jxcontext.getValue(path); +// String referentialName = callbackRequest.getReferentialName(); // -// if (log.isInfoEnabled()) { -// log.info("new value : " + newValue.getTopiaId()); +// for (ReferentialReference<?> referentialReference : callbackRequest.getReferentialsToReplace()) { +// obsoleteReferentialReferences.add(new ObsoleteReferentialReference(referentialName, referentialReference)); // } -// if (log.isDebugEnabled()) { -// log.debug("property to switch " + path + " new : " + -// safeRef); -// log.debug("property to switch " + path + " new Check : " + -// newValue); -// } -// TopiaDAO<TopiaEntity> dao = getDAO(tx, invoker); -// -// dao.update(invoker); // } -// } // -// public String getObsoleteId() { -// return obsoleteId; +// getStepModel().setObsoleteReferences(obsoleteReferentialReferences); +// // } // } -} +} \ No newline at end of file diff --git a/observe-application-swing/src/main/resources/i18n/observe-application-swing_en_GB.properties b/observe-application-swing/src/main/resources/i18n/observe-application-swing_en_GB.properties index 20cd6a9..89c7d0c 100644 --- a/observe-application-swing/src/main/resources/i18n/observe-application-swing_en_GB.properties +++ b/observe-application-swing/src/main/resources/i18n/observe-application-swing_en_GB.properties @@ -1558,6 +1558,7 @@ observe.message.synchro.ref.is.updtodate= observe.message.synchro.referentiel.was.added= observe.message.synchro.referentiel.was.modified= observe.message.synchro.referentiel.was.removed= +observe.message.synchro.referentiel.was.replaced= observe.message.table.editBean.modified= observe.message.table.editBean.modified.but.invalid= observe.message.updating.floatingObject= diff --git a/observe-application-swing/src/main/resources/i18n/observe-application-swing_es_ES.properties b/observe-application-swing/src/main/resources/i18n/observe-application-swing_es_ES.properties index 91d030e..05b92e9 100644 --- a/observe-application-swing/src/main/resources/i18n/observe-application-swing_es_ES.properties +++ b/observe-application-swing/src/main/resources/i18n/observe-application-swing_es_ES.properties @@ -1560,6 +1560,7 @@ observe.message.synchro.ref.is.updtodate=El referencial de la base local está a observe.message.synchro.referentiel.was.added=Se han añadido los datos al referencial remoto (%1$d objeto(s)) observe.message.synchro.referentiel.was.modified=Se han modificado los datos en el referencial remoto (%1$d objeto(s)) observe.message.synchro.referentiel.was.removed=Se han eliminado datos del referencial remoto (%1$d objeto(s)) +observe.message.synchro.referentiel.was.replaced= observe.message.table.editBean.modified=El registro actual se ha modificado y es válido. observe.message.table.editBean.modified.but.invalid=El registro actual se ha modificado pero no es válido. observe.message.updating.floatingObject= diff --git a/observe-application-swing/src/main/resources/i18n/observe-application-swing_fr_FR.properties b/observe-application-swing/src/main/resources/i18n/observe-application-swing_fr_FR.properties index a4e4ff4..fb8556a 100644 --- a/observe-application-swing/src/main/resources/i18n/observe-application-swing_fr_FR.properties +++ b/observe-application-swing/src/main/resources/i18n/observe-application-swing_fr_FR.properties @@ -1547,6 +1547,7 @@ observe.message.synchro.ref.is.updtodate=Le référentiel de la base locale est observe.message.synchro.referentiel.was.added=Des données ont été ajoutées au référentiel distant (%1$d objet(s)) observe.message.synchro.referentiel.was.modified=Des données ont été modifiées sur le référentiel distant (%1$d objet(s)) observe.message.synchro.referentiel.was.removed=Des données ont été supprimées du réferentiel distant (%1$d objet(s)) +observe.message.synchro.referentiel.was.replaced= observe.message.table.editBean.modified=L'entrée en cours d'édition a été modifiée et est valide. observe.message.table.editBean.modified.but.invalid=L'entrée en cours d'édition a été modifiée, mais n'est pas valide. observe.message.updating.floatingObject=L'objet flottant est en cours de modification. -- To stop receiving notification emails like this one, please contact codelutin.com SCM administrator <admin+scm@codelutin.com>.