/* * codjo.net * * Common Apache License 2.0 */ package net.codjo.segmentation.gui.settings; import net.codjo.mad.client.request.Result; import net.codjo.mad.client.request.Row; import net.codjo.mad.gui.request.DetailDataSource; import net.codjo.mad.gui.request.ListDataSource; import java.text.DecimalFormat; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; import java.util.TreeMap; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; /** * Mod�le (i.e. donn�es) de l'arbre contenant les poches. */ public class AxisTreeModel extends DefaultTreeModel { /** * DetailDataSource contenant les donn�es de l'axe. */ private DetailDataSource fatherDataSource; /** * ListDataSource contenant les donn�es des poches. */ private ListDataSource sonDataSource; /** * S�quence utilis�e pour g�n�rer les identifiants temporaires des nouvelles poches */ private int fakeSleeveIdSeq = -1; /** * Map donnant une poche en fonction de sonDataSource id. */ private Map<String, Sleeve> sleeveMap = new HashMap<String, Sleeve>(); private DecimalFormat df = new DecimalFormat("00"); private AxisTree gui; /** * Cr�ation du Model � partir des donn�es contenues dans les DataSources. * * @param fatherDataSourceParam * @param sonDataSourceParam * @param guiParam * * @throws Exception */ public AxisTreeModel(DetailDataSource fatherDataSourceParam, ListDataSource sonDataSourceParam, AxisTree guiParam) throws Exception { super(newClassification(fatherDataSourceParam), true); sonDataSource = sonDataSourceParam; fatherDataSource = fatherDataSourceParam; gui = guiParam; gui.addTreeSelectionListener(new SelectedRowUpdater()); load(); } /** * Creates a tree in which any node can have children. * * @param root a TreeNode object that is the root of the tree * * @see #DefaultTreeModel(javax.swing.tree.TreeNode,boolean) */ public AxisTreeModel(TreeNode root) { super(root); } /** * Creates a tree specifying whether any node can have children, or whether only certain nodes can have * children. * * @param root a TreeNode object that is the root of the tree * @param asksAllowsChildren a boolean, false if any node can have children, true if each node is asked to * see if it can have children * * @see #asksAllowsChildren */ public AxisTreeModel(TreeNode root, boolean asksAllowsChildren) { super(root, asksAllowsChildren); } private void load() { // On parcourt le ListDataSource contenant les donn�es des poches // et on construit une Map d'objets Sleeve, en ordonnant // les poches suivant leur code poche. Result rs = sonDataSource.getLoadResult(); Map<String, Sleeve> treeMap = new TreeMap<String, Sleeve>(new SleeveCodeComparator()); for (int i = 0; i < rs.getRowCount(); i++) { Sleeve sleeve = new Sleeve(rs.getRow(i)); treeMap.put(sleeve.getSleeveCode(), sleeve); } // On construit le Model � partir de la Map, en ins�rant les // poches dans le bon ordre (par code poche croissant). for (String sleeveCode : treeMap.keySet()) { Sleeve sleeve = treeMap.get(sleeveCode); Sleeve parent = parentSleeveForChildCode(sleeveCode); addSleeve(sleeve, parent); } } public int nextFakeSleeveIdSeq() { return fakeSleeveIdSeq--; } private static AxisTreeNode newClassification(DetailDataSource fatherDataSource) { if (fatherDataSource.getLoadResult() == null) { fatherDataSource.setFieldValue("classificationName", Constants.DEFAULT_CLASSIFICATION); } String classificationName = fatherDataSource.getFieldValue("classificationName"); return new AxisTreeNode(classificationName); } /** * Ajoute une feuille au dossier s�lectionn� (une poche qui est un noeud, ou l'axe). * * @param terminal */ public void addNewSleeve(boolean terminal) { TreePath path = gui.getSelectionPath(); DefaultMutableTreeNode lastItem = (DefaultMutableTreeNode)path.getLastPathComponent(); String sleeveName; if (terminal) { sleeveName = Constants.DEFAULT_SLEEVE; } else { sleeveName = Constants.DEFAULT_NODE; } String sleeveCode = buildSleeveCode(lastItem); String classificationId = fatherDataSource.getFieldValue("classificationId"); int sleeveId = this.nextFakeSleeveIdSeq(); Map<String, String> fields = buildSleeveFieldsMap(classificationId, sleeveId, sleeveCode, sleeveName, terminal); Sleeve child = new Sleeve(fields); sonDataSource.addRow(child.getRow()); if (lastItem instanceof AxisTreeNode) { addSleeve(child, null); } else { Sleeve parent = (Sleeve)lastItem; addSleeve(child, parent); nodeStructureChanged(parent); if (!parent.isTerminal()) { gui.expandPath(path); } } } private Map<String, String> buildSleeveFieldsMap(String classificationId, int sleeveId, String sleeveCode, String sleeveName, boolean terminal) { Map<String, String> fields = new HashMap<String, String>(); fields.put("classificationId", classificationId); fields.put("sleeveId", String.valueOf(sleeveId)); fields.put("sleeveRowId", "" + System.currentTimeMillis()); fields.put("sleeveCode", sleeveCode); fields.put("sleeveName", sleeveName); fields.put("sleeveDustbin", String.valueOf(false)); fields.put("terminalElement", String.valueOf(terminal)); fields.put("formula", ""); for (String column : sonDataSource.getColumns()) { if (!fields.containsKey(column)) { fields.put(column, "null"); } } return fields; } /** * Ajoute une poche � une autre poche de ce mod�le. * * @param child la poche � ajouter. * @param parent la poche sur laquelle ajouter <code>child</code>. */ private void addSleeve(Sleeve child, Sleeve parent) { if (parent != null) { insertNodeInto(child, parent, parent.getChildCount()); } else { AxisTreeNode axe = (AxisTreeNode)this.getRoot(); insertNodeInto(child, axe, axe.getChildCount()); } sleeveMap.put(child.getSleeveId(), child); } /** * Retourne le code poche * * @param parentItem * * @return Le code poche (ex: 05-02-2.1) */ private String buildSleeveCode(DefaultMutableTreeNode parentItem) { return df.format(parentItem.getLevel() + 1) + "-" + sleevePath(parentItem); } private String sleevePath(DefaultMutableTreeNode parentItem) { if (parentItem.isRoot()) { // If the sleeveParent of this sleeve is the root (i.e. the AxisTreeNode), // then this sleeve's path is just the position of this sleeve in // the list of its brothers. return "" + (parentItem.getChildCount() + 1); } else { // Else, this sleeve's path is just the path of its sleeveParent // + "." + the position of this sleeve in the list of its brothers. return ((Sleeve)parentItem).getPathCode() + "." + (parentItem.getChildCount() + 1); } } /** * Supprime la poche s�lectionn�e. */ public void removeSleeve() { TreePath path = gui.getSelectionPath(); DefaultMutableTreeNode lastItem = (DefaultMutableTreeNode)path.getLastPathComponent(); if (lastItem instanceof AxisTreeNode) { int nbOfChild = lastItem.getChildCount(); for (int i = nbOfChild - 1; i >= 0; i--) { Sleeve sleeve = (Sleeve)lastItem.getChildAt(i); removeSleeve(sleeve); } } else { Sleeve sleeveToRemove = (Sleeve)lastItem; final DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode)lastItem.getParent(); DefaultMutableTreeNode firstChildToRename = (DefaultMutableTreeNode)parentNode.getChildAfter(lastItem); removeSleeve(sleeveToRemove); updateSleeveCodes(firstChildToRename, parentNode); } } /** * Mise � jour des sleeveCode pour tous les noeuds (et ses fils) apr�s suppression d'un noeud. * * @param childAfter : point de d�part de la mise � jour. * @param parentNode */ private void updateSleeveCodes(DefaultMutableTreeNode childAfter, final DefaultMutableTreeNode parentNode) { while (childAfter != null) { final String newSleevCode = updateSleeveCode(childAfter); final Row row = ((Sleeve)childAfter).getRow(); final int rowIndex = sonDataSource.getLoadResult().getRowIndex(row); sonDataSource.setValue(rowIndex, "sleeveCode", newSleevCode); Enumeration children = childAfter.children(); while (children.hasMoreElements()) { DefaultMutableTreeNode child = (DefaultMutableTreeNode)children.nextElement(); updateSleeveCodes(child, childAfter); } childAfter = (DefaultMutableTreeNode)parentNode.getChildAfter(childAfter); } } private String updateSleeveCode(DefaultMutableTreeNode node) { DefaultMutableTreeNode parentItem = (DefaultMutableTreeNode)node.getParent(); if (parentItem instanceof AxisTreeNode) { return df.format(1) + "-" + (parentItem.getIndex(node) + 1); } else { Sleeve parentSleeve = (Sleeve)parentItem; int indexOfLastPoint = parentSleeve.getSleeveCode().lastIndexOf("-"); final String parentPath = parentSleeve.getSleeveCode().substring(indexOfLastPoint + 1); return df.format(parentItem.getLevel() + 1) + "-" + parentPath + "." + (parentItem.getIndex(node) + 1); } } /** * Supprime une poche de ce Model. * * @param sleeve */ private void removeSleeve(Sleeve sleeve) { removeSleeveFromSonDataSource(sleeve); removeNodeFromParent(sleeve); } /** * Retourne la poche correspondant au code poche donn�, ou <code>null</code> si le code poche fourni est * <code>null</code> (cas de l'axe). * * @param sleeveCode * * @return */ public Sleeve getSleeveByCode(String sleeveCode) { // Cas de la racine if (sleeveCode == null) { return null; } TreeNode node = (TreeNode)this.getRoot(); int caretIndex = sleeveCode.indexOf("-"); String path = sleeveCode.substring(caretIndex + 1); StringTokenizer stknzr = new StringTokenizer(path, "."); while (stknzr.hasMoreTokens()) { String indexStr = stknzr.nextToken(); int index = Integer.parseInt(indexStr); node = node.getChildAt(index - 1); } return (Sleeve)node; } /** * Retourne la poche correspondant � l'id donn�. * * @param sleeveId * * @return */ public Sleeve sleeveForId(int sleeveId) { return sleeveMap.get(String.valueOf(sleeveId)); } /** * Gets the parent <code>Sleeve</code> of the <code>Sleeve</code> corresponding to the given code. Note: * the sleeve with code <code>childCode</code> does not need to be already attached to this * <code>AxisTreeModel</code>. * * @param childCode * * @return */ public Sleeve parentSleeveForChildCode(String childCode) { String parentSleeveCode = parentCodeForChildCode(childCode); return getSleeveByCode(parentSleeveCode); } private String parentCodeForChildCode(String childCode) { int firstIndex = childCode.indexOf("-"); int level = Integer.parseInt(childCode.substring(0, firstIndex)); String parentCode; if (level > 1) { int indexOfLastPoint = childCode.lastIndexOf("."); parentCode = df.format(level - 1) + childCode.substring(firstIndex, indexOfLastPoint); } else { parentCode = null; } return parentCode; } /** * Supprime une poche et ses enfants de la ListDataSource. * * @param sleeve */ private void removeSleeveFromSonDataSource(Sleeve sleeve) { // First, remove the children of this sleeve int nbOfChildren = sleeve.getChildCount(); for (int i = nbOfChildren - 1; i >= 0; i--) { Sleeve childSleeve = (Sleeve)sleeve.getChildAt(i); removeSleeveFromSonDataSource(childSleeve); } // Finally, remove the sleeve itself for (int i = 0; i < sonDataSource.getRowCount(); i++) { if (sonDataSource.containsField(i, "sleeveId")) { if (sonDataSource.getValueAt(i, "sleeveId").equals(String.valueOf( sleeve.getSleeveId()))) { sonDataSource.removeRow(i); } } } sonDataSource.setSelectedRow(null); } public boolean hasDustBin() { return hasDustBinUnder((TreeNode)getRoot()); } private boolean hasDustBinUnder(TreeNode node) { if (node instanceof Sleeve && ((Sleeve)node).isDustbin()) { return true; } else { for (int i = 0; i < node.getChildCount(); i++) { if (hasDustBinUnder(node.getChildAt(i))) { return true; } } return false; } } public String getSleeveNameWithoutFormula() { return getSleeveWithoutFormula((TreeNode)getRoot()); } private String getSleeveWithoutFormula(TreeNode node) { if (node instanceof Sleeve) { Sleeve sleeve = (Sleeve)node; if (!sleeve.isDustbin() && sleeve.isTerminal() && "null".equals(sleeve.getFormula())) { return node.toString(); } } for (int i = 0; i < node.getChildCount(); i++) { String guilty = getSleeveWithoutFormula(node.getChildAt(i)); if (guilty != null) { return guilty; } } return null; } public String findDoubleSleeveName() { Set<String> names = new HashSet<String>(); return findDoubleSleeveName(names, (TreeNode)getRoot()); } private String findDoubleSleeveName(Set<String> names, TreeNode node) { if (node instanceof Sleeve) { final String sleeveName = ((Sleeve)node).getSleeveName(); if (names.contains(sleeveName)) { return node.toString(); } names.add(sleeveName); } for (int i = 0; i < node.getChildCount(); i++) { String guilty = findDoubleSleeveName(names, node.getChildAt(i)); if (guilty != null) { return guilty; } } return null; } private class SelectedRowUpdater implements TreeSelectionListener { private boolean isUpdating = false; public void valueChanged(TreeSelectionEvent event) { if (isUpdating) { return; } isUpdating = true; try { TreePath selectionPath = gui.getSelectionPath(); if (selectionPath != null) { Object obj = selectionPath.getLastPathComponent(); if (obj instanceof Sleeve) { // Cas o� une poche est s�lectionn�e Sleeve selectedSleeve = (Sleeve)obj; selectedSleeve.getSleeveId(); Row selectedRow = selectedSleeve.getRow(); sonDataSource.setSelectedRow(selectedRow); } else { sonDataSource.setSelectedRow(null); } } } finally { isUpdating = false; } } } }