Author: sletellier Date: 2010-06-29 18:09:09 +0200 (Tue, 29 Jun 2010) New Revision: 2009 Url: http://nuiton.org/repositories/revision/jaxx/2009 Log: Add tree table support Added: trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/JaxxTreeTableDemoModel.java trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/JaxxTreeTableDemoNode.java trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/DataProvider.java trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/JaxxDelegateTreeModel.java trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/JaxxNode.java trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/JaxxNodeChildLoador.java trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/package.html trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/tree/AbstractJaxxTreeCellRenderer.java trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/tree/JaxxTreeHelper.java trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/tree/JaxxTreeNode.java trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/tree/JaxxTreeNodeChildLoador.java trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/treetable/JaxxTreeTableModel.java trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/treetable/JaxxTreeTableNode.java trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/treetable/JaxxTreeTableNodeChildLoador.java Removed: trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/tree/ Modified: trunk/jaxx-compiler/changelog.txt trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/JaxxTreeDemo.jaxx trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/JaxxTreeDemoCellRenderer.java trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/JaxxTreeDemoDataProvider.java trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/JaxxTreeDemoHelper.java trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/JaxxTreeDemoNode.java trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/content/AbstractContentUI.jaxx trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/content/ActorsContentUI.jaxx trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/content/MoviesContentUI.jaxx trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/loadors/ActorsNodeLoadors.java trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/loadors/MoviesNodeLoadors.java trunk/jaxx-demo/src/main/java/jaxx/demo/tree/DemoCellRenderer.java trunk/jaxx-demo/src/main/java/jaxx/demo/tree/DemoDataProvider.java trunk/jaxx-demo/src/main/java/jaxx/demo/tree/DemoNode.java trunk/jaxx-demo/src/main/java/jaxx/demo/tree/DemoNodeLoador.java trunk/jaxx-demo/src/main/java/jaxx/demo/tree/DemoTreeHelper.java trunk/jaxx-demo/src/main/resources/i18n/jaxx-demo-en_GB.properties trunk/jaxx-demo/src/main/resources/i18n/jaxx-demo-fr_FR.properties trunk/jaxx-demo/src/main/resources/log4j.properties trunk/jaxx-runtime/changelog.txt trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/navigation/treetable/NavigationTreeTableNode.java Modified: trunk/jaxx-compiler/changelog.txt =================================================================== --- trunk/jaxx-compiler/changelog.txt 2010-06-29 15:33:32 UTC (rev 2008) +++ trunk/jaxx-compiler/changelog.txt 2010-06-29 16:09:09 UTC (rev 2009) @@ -42,10 +42,10 @@ - improve classloader managment - keep in DataSource objetCode - fix bug when processDataBinding on a null objectCode - - always clean node cached values when selected it + - always clean demoNode cached values when selected it - add usefull databinding method in Util - * 20081213 [chemit] - improve navigation tree node rendering with some caches + * 20081213 [chemit] - improve navigation tree demoNode rendering with some caches - introduce a ChildBuilder to simplify building of child nodes from a collection or array 0.7 chemit 20081210 @@ -67,7 +67,7 @@ - only enter once in $initialize method in generated code 0.6 chemit 20081117 - * 20081118 [chemit] introduce NavigationUtil, save in context selected node + * 20081118 [chemit] introduce NavigationUtil, save in context selected demoNode * 20081107 [chemit] improve data binding and code generation : - make possible inheritance in binding - add an attribute javaBean to an object : will generate a full java bean support property Modified: trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/JaxxTreeDemo.jaxx =================================================================== --- trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/JaxxTreeDemo.jaxx 2010-06-29 15:33:32 UTC (rev 2008) +++ trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/JaxxTreeDemo.jaxx 2010-06-29 16:09:09 UTC (rev 2009) @@ -25,23 +25,26 @@ <jaxx.demo.DemoPanel layout='{new BorderLayout()}'> <import> - jaxx.runtime.decorator.DecoratorProvider - jaxx.demo.component.jaxx.tree.content.ActorContentUI - jaxx.demo.component.jaxx.tree.content.ActorsContentUI - jaxx.demo.component.jaxx.tree.content.MovieContentUI - jaxx.demo.component.jaxx.tree.content.MoviesContentUI - jaxx.demo.entities.Movie - jaxx.demo.entities.People - javax.swing.tree.TreePath - java.lang.reflect.Constructor - jaxx.demo.component.jaxx.tree.content.AbstractContentUI + jaxx.runtime.decorator.DecoratorProvider + jaxx.demo.component.jaxx.tree.content.ActorContentUI + jaxx.demo.component.jaxx.tree.content.ActorsContentUI + jaxx.demo.component.jaxx.tree.content.MovieContentUI + jaxx.demo.component.jaxx.tree.content.MoviesContentUI + jaxx.demo.entities.Movie + jaxx.demo.entities.People + javax.swing.tree.TreePath + java.lang.reflect.Constructor + jaxx.demo.component.jaxx.tree.content.AbstractContentUI + org.jdesktop.swingx.JXTreeTable + jaxx.runtime.swing.nav.JaxxNode </import> <CardLayout2 id='contentLayout'/> <script><![CDATA[ private JaxxTreeDemoDataProvider dataProvider = new JaxxTreeDemoDataProvider(); -private JaxxTreeDemoHelper helper = new JaxxTreeDemoHelper(dataProvider); +private JaxxTreeDemoHelper<JaxxTreeDemoNode> treeHelper = new JaxxTreeDemoHelper<JaxxTreeDemoNode>(dataProvider); +private JaxxTreeDemoHelper<JaxxTreeTableDemoNode> treeTableHelper = new JaxxTreeDemoHelper<JaxxTreeTableDemoNode>(dataProvider); @Override protected String[] getSources() { @@ -49,86 +52,111 @@ "JaxxTreeDemoHelper.java", "JaxxTreeDemoNode.java", "JaxxTreeDemoCellRenderer.java", - "JaxxTreeDemoDataProvider.java", + "JaxxTreeTableDemoModel.java", + "JaxxTreeTableDemoNode.java", "loadors/ActorsNodeLoadors.java", "loadors/MoviesNodeLoadors.java"}; } private void $afterCompleteSetup() { - setContextValue(helper); + setContextValue(treeTableHelper, "treeHelper"); + setContextValue(treeTableHelper, "treeTableHelper"); // Creation of selection listener to open ui when tree selection change - TreeSelectionListener selectionListener = new TreeSelectionListener() { + TreeSelectionListener treeSelectionListener = new TreeSelectionListener() { @Override public void valueChanged(TreeSelectionEvent event) { TreePath path = event.getPath(); - JaxxTreeDemoNode node = (JaxxTreeDemoNode) path.getLastPathComponent(); + JaxxTreeDemoNode demoNode = (JaxxTreeDemoNode) path.getLastPathComponent(); if (log.isDebugEnabled()) { - log.debug("Select node " + node); + log.debug("Select demoNode " + demoNode); } // Do nothing for root - if (node.isRoot()) { + if (demoNode.isRoot()) { return; } - openUI(node); + openUI(demoNode); } }; + // Creation of selection listener to open ui when tree selection change + TreeSelectionListener treeTableSelectionListener = new TreeSelectionListener() { + @Override + public void valueChanged(TreeSelectionEvent event) { + TreePath path = event.getPath(); + JaxxTreeTableDemoNode demoNode = (JaxxTreeTableDemoNode) path.getLastPathComponent(); + + if (log.isDebugEnabled()) { + log.debug("Select demoNode " + demoNode); + } + + // Do nothing for root + if (demoNode.isRoot()) { + return; + } + openUI(demoNode); + } + }; + // Register decorator DecoratorProvider decoratorProvider = getContextValue(DecoratorProvider.class); // Attach renderer - navigation.setCellRenderer(new JaxxTreeDemoCellRenderer(decoratorProvider, dataProvider)); + navigationTree.setCellRenderer(new JaxxTreeDemoCellRenderer(decoratorProvider, dataProvider)); // Register tree - helper.setTree(navigation, true, selectionListener); + treeHelper.setTree(navigationTree, true, treeSelectionListener); + treeTableHelper.setTreeTable(navigationTreeTable, true, treeTableSelectionListener); + SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - navigation.setSelectionInterval(0, 0); + navigationTree.setSelectionInterval(0, 0); splitPane.resetToPreferredSizes(); } }); // expand the tree - SwingUtil.expandTree(navigation); - - // auto-expand node when selected - SwingUtil.addExpandOnClickListener(navigation); + SwingUtil.expandTree(navigationTree); + SwingUtil.expandTreeTable(navigationTreeTable); + + // auto-expand demoNode when selected + SwingUtil.addExpandOnClickListener(navigationTree); + SwingUtil.addExpandOnClickListener(navigationTreeTable); } -protected void openUI(JaxxTreeDemoNode node) { +protected void openUI(JaxxNode demoNode) { - // Get node type - Class<?> editType = node.getInternalClass(); - String id = node.getId(); + // Get demoNode type + Class<?> editType = demoNode.getInternalClass(); + String id = demoNode.getId(); - // If it's category node + // If it's category demoNode if (editType.equals(String.class)) { - // Actors categorie node + // Actors categorie demoNode if (JaxxTreeDemoHelper.ACTORS_CATEGORY_NODE.equals(id)) { java.util.List<People> peoples = dataProvider.getPeoples(); showUI(peoples, ActorsContentUI.class); - // Movies categorie node + // Movies categorie demoNode } else if (JaxxTreeDemoHelper.MOVIES_CATEGORY_NODE.equals(id)) { java.util.List<Movie> movies = dataProvider.getMovies(); showUI(movies, MoviesContentUI.class); } - // People node + // People demoNode } else if (editType.equals(People.class)) { People people = dataProvider.getPeople(id); showUI(people, ActorContentUI.class); - // Movie node + // Movie demoNode } else if (editType.equals(Movie.class)) { Movie movie = dataProvider.getMovie(id); showUI(movie, MovieContentUI.class); @@ -186,18 +214,35 @@ constraints='BorderLayout.CENTER' oneTouchExpandable='true'> - <JScrollPane border='{null}' - horizontalScrollBarPolicy='{JScrollPane.HORIZONTAL_SCROLLBAR_NEVER}' - verticalScrollBarPolicy='{JScrollPane.VERTICAL_SCROLLBAR_NEVER}'> + <JTabbedPane> + <tab title='jaxxdemo.tree.tabtitle'> + <JScrollPane border='{null}' + horizontalScrollBarPolicy='{JScrollPane.HORIZONTAL_SCROLLBAR_NEVER}' + verticalScrollBarPolicy='{JScrollPane.VERTICAL_SCROLLBAR_NEVER}'> - <JTree id="navigation" - font-size='11' - rootVisible='false' - showsRootHandles='false' - model='{helper.createTreeModel()}'/> + <JTree id="navigationTree" + font-size='11' + rootVisible='false' + showsRootHandles='false' + model='{treeHelper.createTreeModel()}'/> - </JScrollPane> + </JScrollPane> + </tab> + <tab title='jaxxdemo.treeTable.tabtitle'> + <JScrollPane border='{null}' + horizontalScrollBarPolicy='{JScrollPane.HORIZONTAL_SCROLLBAR_NEVER}' + verticalScrollBarPolicy='{JScrollPane.VERTICAL_SCROLLBAR_NEVER}'> + <JXTreeTable id="navigationTreeTable" + font-size='11' + rootVisible='false' + showsRootHandles='false' + treeTableModel='{treeTableHelper.createTreeTableModel()}'/> + + </JScrollPane> + </tab> + </JTabbedPane> + <JPanel id="content" layout="{contentLayout}"/> </JSplitPane> Modified: trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/JaxxTreeDemoCellRenderer.java =================================================================== --- trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/JaxxTreeDemoCellRenderer.java 2010-06-29 15:33:32 UTC (rev 2008) +++ trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/JaxxTreeDemoCellRenderer.java 2010-06-29 16:09:09 UTC (rev 2009) @@ -29,7 +29,7 @@ import jaxx.demo.entities.People; import jaxx.runtime.decorator.Decorator; import jaxx.runtime.decorator.DecoratorProvider; -import jaxx.runtime.swing.tree.AbstractJaxxTreeCellRenderer; +import jaxx.runtime.swing.nav.tree.AbstractJaxxTreeCellRenderer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; Modified: trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/JaxxTreeDemoDataProvider.java =================================================================== --- trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/JaxxTreeDemoDataProvider.java 2010-06-29 15:33:32 UTC (rev 2008) +++ trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/JaxxTreeDemoDataProvider.java 2010-06-29 16:09:09 UTC (rev 2009) @@ -27,7 +27,7 @@ import jaxx.demo.entities.Movie; import jaxx.demo.entities.People; -import jaxx.runtime.swing.tree.DataProvider; +import jaxx.runtime.swing.nav.DataProvider; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; Modified: trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/JaxxTreeDemoHelper.java =================================================================== --- trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/JaxxTreeDemoHelper.java 2010-06-29 15:33:32 UTC (rev 2008) +++ trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/JaxxTreeDemoHelper.java 2010-06-29 16:09:09 UTC (rev 2009) @@ -27,10 +27,12 @@ import jaxx.demo.component.jaxx.tree.loadors.ActorsNodeLoadors; import jaxx.demo.component.jaxx.tree.loadors.MoviesNodeLoadors; -import jaxx.runtime.swing.tree.JaxxTreeHelper; +import jaxx.runtime.swing.nav.JaxxNode; +import jaxx.runtime.swing.nav.tree.JaxxTreeHelper; +import jaxx.runtime.swing.nav.treetable.JaxxTreeTableModel; +import jaxx.runtime.swing.nav.JaxxDelegateTreeModel; import javax.swing.tree.DefaultTreeModel; -import javax.swing.tree.TreeModel; import static org.nuiton.i18n.I18n.n_; @@ -38,7 +40,7 @@ * @author sletellier <letellier@codelutin.com> * @since 2.1 */ -public class JaxxTreeDemoHelper extends JaxxTreeHelper<JaxxTreeDemoNode> { +public class JaxxTreeDemoHelper<N extends JaxxNode<N>> extends JaxxTreeHelper<N> { public static String MOVIES_CATEGORY_NODE = "movies"; @@ -48,10 +50,16 @@ setDataProvider(provider); } - public TreeModel createTreeModel() { + @Override + public JaxxTreeDemoDataProvider getDataProvider() { + return (JaxxTreeDemoDataProvider)dataProvider; + } + @SuppressWarnings({"unchecked"}) + public DefaultTreeModel createTreeModel() { + // Create root static node - JaxxTreeDemoNode root = new JaxxTreeDemoNode( + JaxxNode root = new JaxxTreeDemoNode( String.class, "Root node", null, @@ -59,19 +67,19 @@ ); // Create movies category node - JaxxTreeDemoNode moviesCategoryNode = new JaxxTreeDemoNode( + JaxxNode moviesCategoryNode = new JaxxTreeDemoNode( String.class, n_(MOVIES_CATEGORY_NODE), null, - getChildLoador(MoviesNodeLoadors.class) + new MoviesNodeLoadors<JaxxTreeDemoNode>() ); // Create peoples category node - JaxxTreeDemoNode peoplesCategoryNode = new JaxxTreeDemoNode( + JaxxNode peoplesCategoryNode = new JaxxTreeDemoNode( String.class, n_(ACTORS_CATEGORY_NODE), null, - getChildLoador(ActorsNodeLoadors.class) + new ActorsNodeLoadors<JaxxTreeDemoNode>() ); // Add to root @@ -79,11 +87,52 @@ root.add(peoplesCategoryNode); // Create model - DefaultTreeModel model = createModel(root); - + JaxxDelegateTreeModel<N> model = createTreeModel((N) root); + // Populate childs nodes root.populateChilds(model, getDataProvider()); - return model; + return getTreeModel(); } + + @SuppressWarnings({"unchecked"}) + public JaxxTreeTableModel createTreeTableModel() { + + // Create root static node + JaxxNode root = new JaxxTreeTableDemoNode( + String.class, + "Root node", + null, + null + ); + + // Create movies category node + JaxxNode moviesCategoryNode = new JaxxTreeTableDemoNode( + String.class, + n_(MOVIES_CATEGORY_NODE), + null, + new MoviesNodeLoadors<JaxxTreeTableDemoNode>(true) + ); + + // Create peoples category node + JaxxNode peoplesCategoryNode = new JaxxTreeTableDemoNode( + String.class, + n_(ACTORS_CATEGORY_NODE), + null, + new ActorsNodeLoadors<JaxxTreeTableDemoNode>(true) + ); + + // Add to root + root.add(moviesCategoryNode); + root.add(peoplesCategoryNode); + + // Create model + JaxxTreeTableDemoModel delegate = new JaxxTreeTableDemoModel(getDataProvider()); + JaxxDelegateTreeModel model = createTreeTableModel(delegate, (N) root); + + // Populate childs nodes + root.populateChilds(model, getDataProvider()); + + return getTreeTableModel(); + } } Modified: trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/JaxxTreeDemoNode.java =================================================================== --- trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/JaxxTreeDemoNode.java 2010-06-29 15:33:32 UTC (rev 2008) +++ trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/JaxxTreeDemoNode.java 2010-06-29 16:09:09 UTC (rev 2009) @@ -25,14 +25,14 @@ package jaxx.demo.component.jaxx.tree; -import jaxx.runtime.swing.tree.JaxxNode; -import jaxx.runtime.swing.tree.JaxxNodeChildLoador; +import jaxx.runtime.swing.nav.JaxxNodeChildLoador; +import jaxx.runtime.swing.nav.tree.JaxxTreeNode; /** * @author sletellier <letellier@codelutin.com> * @since 2.1 */ -public class JaxxTreeDemoNode extends JaxxNode<JaxxTreeDemoNode> { +public class JaxxTreeDemoNode extends JaxxTreeNode<JaxxTreeDemoNode> { private static final long serialVersionUID = 1L; @@ -43,7 +43,7 @@ public JaxxTreeDemoNode(Class<?> internalClass, String id, String context, - JaxxNodeChildLoador<?, ?, JaxxTreeDemoNode> loador) { + JaxxNodeChildLoador<?, ?, JaxxTreeDemoNode> loador) { super(internalClass, id, context, loador); } } Added: trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/JaxxTreeTableDemoModel.java =================================================================== --- trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/JaxxTreeTableDemoModel.java (rev 0) +++ trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/JaxxTreeTableDemoModel.java 2010-06-29 16:09:09 UTC (rev 2009) @@ -0,0 +1,114 @@ +/* + * #%L + * JAXX :: Demo + * + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2008 - 2010 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package jaxx.demo.component.jaxx.tree; + +import jaxx.demo.entities.Movie; +import jaxx.demo.entities.People; +import jaxx.runtime.swing.nav.treetable.JaxxTreeTableModel; +import jaxx.runtime.swing.nav.JaxxNode; + +import static org.nuiton.i18n.I18n._; + +public class JaxxTreeTableDemoModel extends JaxxTreeTableModel.MyDefaultTreeTableModel { + + protected JaxxTreeDemoDataProvider dataProvider; + + public JaxxTreeTableDemoModel(JaxxTreeDemoDataProvider dataProvider) { + this.dataProvider = dataProvider; + } + + @Override + public Object getValueAt(Object o, int i) { + JaxxNode node = (JaxxNode)o; + + // Get node type + Class<?> editType = node.getInternalClass(); + String id = node.getId(); + + // If it's category node + if (node.isStringNode()) { + if (i == 0) { + return _(id); + } + return ""; + + // People node + } else if (editType.equals(People.class)) { + People people = dataProvider.getPeople(id); + return getPeopleColumn(people, i); + + // Movie node + } else if (editType.equals(Movie.class)) { + Movie movie = dataProvider.getMovie(id); + return getMovieColumn(movie, i); + } + + // This never append + return "not found"; + } + + private String getMovieColumn(Movie movie, int i) { + String result = ""; + + switch (i) { + case 0: + result = movie.getTitle(); + break; + case 2: + result = String.valueOf(movie.getYear()); + break; + } + return result; + } + + protected String getPeopleColumn(People people, int i) { + String result = ""; + + switch (i) { + case 0: + result = people.getFirstName(); + break; + case 1: + result = people.getLastName(); + break; + case 2: + result = String.valueOf(people.getAge()); + break; + } + return result; + } + + @Override + public String[] getColumnsNames() { + return new String[]{_("jaxxdemo.tree.firstName"), + _("jaxxdemo.tree.lastName"), + _("jaxxdemo.tree.age")}; + } + + @Override + public boolean isCellEditable(Object node, int column) { + return false; + } +} Added: trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/JaxxTreeTableDemoNode.java =================================================================== --- trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/JaxxTreeTableDemoNode.java (rev 0) +++ trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/JaxxTreeTableDemoNode.java 2010-06-29 16:09:09 UTC (rev 2009) @@ -0,0 +1,39 @@ +/* + * #%L + * JAXX :: Demo + * + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2008 - 2010 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package jaxx.demo.component.jaxx.tree; + +import jaxx.runtime.swing.nav.JaxxNodeChildLoador; +import jaxx.runtime.swing.nav.treetable.JaxxTreeTableNode; + +public class JaxxTreeTableDemoNode extends JaxxTreeTableNode<JaxxTreeTableDemoNode> { + + protected JaxxTreeTableDemoNode(String id) { + super(id); + } + + public JaxxTreeTableDemoNode(Class<?> internalClass, String id, String context, JaxxNodeChildLoador childLoador) { + super(internalClass, id, context, childLoador); + } +} Modified: trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/content/AbstractContentUI.jaxx =================================================================== --- trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/content/AbstractContentUI.jaxx 2010-06-29 15:33:32 UTC (rev 2008) +++ trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/content/AbstractContentUI.jaxx 2010-06-29 16:09:09 UTC (rev 2009) @@ -30,9 +30,12 @@ import jaxx.demo.component.jaxx.tree.JaxxTreeDemoHelper; // Get helper in context -JaxxTreeDemoHelper getHelper() { - return getContextValue(JaxxTreeDemoHelper.class); +JaxxTreeDemoHelper getTreeHelper() { + return getContextValue(JaxxTreeDemoHelper.class, "treeHelper"); } +JaxxTreeDemoHelper getTreeTableHelper() { + return getContextValue(JaxxTreeDemoHelper.class, "treeTableHelper"); +} public abstract B getData(); Modified: trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/content/ActorsContentUI.jaxx =================================================================== --- trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/content/ActorsContentUI.jaxx 2010-06-29 15:33:32 UTC (rev 2008) +++ trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/content/ActorsContentUI.jaxx 2010-06-29 16:09:09 UTC (rev 2009) @@ -55,7 +55,8 @@ protected void selectChild() { People selected = (People)list.getSelectedValue(); - getHelper().selectNode(selected.getId()); + getTreeHelper().selectNode(selected.getId()); + getTreeTableHelper().selectNode(selected.getId()); } ]]></script> Modified: trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/content/MoviesContentUI.jaxx =================================================================== --- trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/content/MoviesContentUI.jaxx 2010-06-29 15:33:32 UTC (rev 2008) +++ trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/content/MoviesContentUI.jaxx 2010-06-29 16:09:09 UTC (rev 2009) @@ -58,7 +58,8 @@ protected void selectChild() { Movie selected = (Movie)list.getSelectedValue(); - getHelper().selectNode(selected.getId()); + getTreeHelper().selectNode(selected.getId()); + getTreeTableHelper().selectNode(selected.getId()); } ]]></script> Modified: trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/loadors/ActorsNodeLoadors.java =================================================================== --- trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/loadors/ActorsNodeLoadors.java 2010-06-29 15:33:32 UTC (rev 2008) +++ trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/loadors/ActorsNodeLoadors.java 2010-06-29 16:09:09 UTC (rev 2009) @@ -25,11 +25,13 @@ package jaxx.demo.component.jaxx.tree.loadors; +import jaxx.demo.component.jaxx.tree.JaxxTreeTableDemoNode; import jaxx.demo.component.jaxx.tree.JaxxTreeDemoDataProvider; import jaxx.demo.component.jaxx.tree.JaxxTreeDemoNode; import jaxx.demo.entities.People; -import jaxx.runtime.swing.tree.DataProvider; -import jaxx.runtime.swing.tree.JaxxNodeChildLoador; +import jaxx.runtime.swing.nav.DataProvider; +import jaxx.runtime.swing.nav.JaxxNode; +import jaxx.runtime.swing.nav.JaxxNodeChildLoador; import java.util.List; @@ -37,12 +39,19 @@ * @author sletellier <letellier@codelutin.com> * @since 2.1 */ -public class ActorsNodeLoadors extends JaxxNodeChildLoador<People,People, JaxxTreeDemoNode> { +public class ActorsNodeLoadors<N extends JaxxNode<N>> extends JaxxNodeChildLoador<People,People, N> { private static final long serialVersionUID = 1L; + protected boolean isTreeTable; + public ActorsNodeLoadors() { + this(false); + } + + public ActorsNodeLoadors(boolean isTreeTable) { super(People.class); + this.isTreeTable = isTreeTable; } @Override @@ -64,17 +73,29 @@ return provider.getPeoples(); } + @SuppressWarnings({"unchecked"}) @Override - public JaxxTreeDemoNode createNode(People data, DataProvider dataProvider) { + public N createNode(People data, DataProvider dataProvider) { + JaxxNode actorNode; + // Create actor static nodes - JaxxTreeDemoNode actorsNode = new JaxxTreeDemoNode( - getBeanType(), - data.getId(), - null, - null - ); + if (!isTreeTable) { + actorNode = new JaxxTreeDemoNode( + getBeanType(), + data.getId(), + null, + null + ); + } else { + actorNode = new JaxxTreeTableDemoNode( + getBeanType(), + data.getId(), + null, + null + ); + } - return actorsNode; + return (N) actorNode; } } Modified: trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/loadors/MoviesNodeLoadors.java =================================================================== --- trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/loadors/MoviesNodeLoadors.java 2010-06-29 15:33:32 UTC (rev 2008) +++ trunk/jaxx-demo/src/main/java/jaxx/demo/component/jaxx/tree/loadors/MoviesNodeLoadors.java 2010-06-29 16:09:09 UTC (rev 2009) @@ -28,9 +28,11 @@ import jaxx.demo.component.jaxx.tree.JaxxTreeDemoDataProvider; import jaxx.demo.component.jaxx.tree.JaxxTreeDemoHelper; import jaxx.demo.component.jaxx.tree.JaxxTreeDemoNode; +import jaxx.demo.component.jaxx.tree.JaxxTreeTableDemoNode; import jaxx.demo.entities.Movie; -import jaxx.runtime.swing.tree.DataProvider; -import jaxx.runtime.swing.tree.JaxxNodeChildLoador; +import jaxx.runtime.swing.nav.JaxxNode; +import jaxx.runtime.swing.nav.JaxxNodeChildLoador; +import jaxx.runtime.swing.nav.DataProvider; import java.util.List; @@ -40,12 +42,19 @@ * @author sletellier <letellier@codelutin.com> * @since 2.1 */ -public class MoviesNodeLoadors extends JaxxNodeChildLoador<Movie, Movie, JaxxTreeDemoNode> { +public class MoviesNodeLoadors<N extends JaxxNode<N>> extends JaxxNodeChildLoador<Movie, Movie, N> { private static final long serialVersionUID = 1L; + protected boolean isTreeTable; + public MoviesNodeLoadors() { + this(false); + } + + public MoviesNodeLoadors(boolean isTreeTable) { super(Movie.class); + this.isTreeTable = isTreeTable; } @Override @@ -59,28 +68,50 @@ return provider.getMovies(); } + @SuppressWarnings({"unchecked"}) @Override - public JaxxTreeDemoNode createNode(Movie data, DataProvider dataProvider) { + public N createNode(Movie data, DataProvider dataProvider) { - // Create movies static nodes - JaxxTreeDemoNode moviesNode = new JaxxTreeDemoNode( - getBeanType(), - data.getId(), - null, - null - ); + JaxxNode moviesNode; + JaxxNode actorsCategoryNode; - // Create clients category node - JaxxTreeDemoNode actorsCategoryNode = new JaxxTreeDemoNode( - String.class, - n_(JaxxTreeDemoHelper.ACTORS_CATEGORY_NODE), - null, - JaxxTreeDemoHelper.getChildLoador(ActorsNodeLoadors.class) - ); + if (!isTreeTable) { + // Create movies static nodes + moviesNode = new JaxxTreeDemoNode( + getBeanType(), + data.getId(), + null, + null + ); + // Create clients category node + actorsCategoryNode = new JaxxTreeDemoNode( + String.class, + n_(JaxxTreeDemoHelper.ACTORS_CATEGORY_NODE), + null, + new ActorsNodeLoadors<JaxxTreeDemoNode>(false) + ); + } else { + // Create movies static nodes + moviesNode = new JaxxTreeTableDemoNode( + getBeanType(), + data.getId(), + null, + null + ); + + // Create clients category node + actorsCategoryNode = new JaxxTreeTableDemoNode( + String.class, + n_(JaxxTreeDemoHelper.ACTORS_CATEGORY_NODE), + null, + new ActorsNodeLoadors<JaxxTreeDemoNode>(true) + ); + } + // Add actors nodes to movies node moviesNode.add(actorsCategoryNode); - return moviesNode; + return (N) moviesNode; } } Modified: trunk/jaxx-demo/src/main/java/jaxx/demo/tree/DemoCellRenderer.java =================================================================== --- trunk/jaxx-demo/src/main/java/jaxx/demo/tree/DemoCellRenderer.java 2010-06-29 15:33:32 UTC (rev 2008) +++ trunk/jaxx-demo/src/main/java/jaxx/demo/tree/DemoCellRenderer.java 2010-06-29 16:09:09 UTC (rev 2009) @@ -26,7 +26,7 @@ package jaxx.demo.tree; import jaxx.demo.component.jaxx.tree.JaxxTreeDemoDataProvider; -import jaxx.runtime.swing.tree.AbstractJaxxTreeCellRenderer; +import jaxx.runtime.swing.nav.tree.AbstractJaxxTreeCellRenderer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; Modified: trunk/jaxx-demo/src/main/java/jaxx/demo/tree/DemoDataProvider.java =================================================================== --- trunk/jaxx-demo/src/main/java/jaxx/demo/tree/DemoDataProvider.java 2010-06-29 15:33:32 UTC (rev 2008) +++ trunk/jaxx-demo/src/main/java/jaxx/demo/tree/DemoDataProvider.java 2010-06-29 16:09:09 UTC (rev 2009) @@ -56,7 +56,7 @@ import jaxx.demo.fun.CalculatorDemo; import jaxx.demo.fun.CounterDemo; import jaxx.demo.fun.LabelStyleDemo; -import jaxx.runtime.swing.tree.DataProvider; +import jaxx.runtime.swing.nav.DataProvider; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; Modified: trunk/jaxx-demo/src/main/java/jaxx/demo/tree/DemoNode.java =================================================================== --- trunk/jaxx-demo/src/main/java/jaxx/demo/tree/DemoNode.java 2010-06-29 15:33:32 UTC (rev 2008) +++ trunk/jaxx-demo/src/main/java/jaxx/demo/tree/DemoNode.java 2010-06-29 16:09:09 UTC (rev 2009) @@ -25,7 +25,7 @@ package jaxx.demo.tree; -import jaxx.runtime.swing.tree.JaxxNode; +import jaxx.runtime.swing.nav.tree.JaxxTreeNode; /** * Basic node of the demo. @@ -33,7 +33,7 @@ * @author tchemit <chemit@codelutin.com> * @since 2.1 */ -public class DemoNode extends JaxxNode<DemoNode> { +public class DemoNode extends JaxxTreeNode<DemoNode> { private static final long serialVersionUID = 1L; Modified: trunk/jaxx-demo/src/main/java/jaxx/demo/tree/DemoNodeLoador.java =================================================================== --- trunk/jaxx-demo/src/main/java/jaxx/demo/tree/DemoNodeLoador.java 2010-06-29 15:33:32 UTC (rev 2008) +++ trunk/jaxx-demo/src/main/java/jaxx/demo/tree/DemoNodeLoador.java 2010-06-29 16:09:09 UTC (rev 2009) @@ -25,8 +25,11 @@ package jaxx.demo.tree; -import jaxx.runtime.swing.tree.DataProvider; -import jaxx.runtime.swing.tree.JaxxNodeChildLoador; +import jaxx.runtime.swing.nav.tree.JaxxTreeNodeChildLoador; +import jaxx.runtime.swing.nav.DataProvider; +import jaxx.runtime.swing.nav.JaxxNodeChildLoador; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import java.util.List; @@ -34,10 +37,13 @@ * @author tchemit <tchemit@codelutin.com> * @since 2.1 */ -public class DemoNodeLoador extends JaxxNodeChildLoador<Object,Object, DemoNode> { +public class DemoNodeLoador extends JaxxTreeNodeChildLoador<Object,Object, DemoNode> { private static final long serialVersionUID = 1L; + /** Logger */ + static private final Log log = LogFactory.getLog(JaxxNodeChildLoador.class); + public DemoNodeLoador() { super(Object.class); } @@ -55,6 +61,10 @@ @Override public DemoNode createNode(Object data, DataProvider dataProvider) { + if (log.isDebugEnabled()) { + log.debug("Creating node for object : " + data); + } + DemoNode node = null; if (data instanceof String) { @@ -68,8 +78,7 @@ } if (node == null) { - throw new IllegalArgumentException( - "Data [" + data + "] can not be use to build a node"); + throw new IllegalArgumentException("Data [" + data + "] can not be use to build a node"); } Modified: trunk/jaxx-demo/src/main/java/jaxx/demo/tree/DemoTreeHelper.java =================================================================== --- trunk/jaxx-demo/src/main/java/jaxx/demo/tree/DemoTreeHelper.java 2010-06-29 15:33:32 UTC (rev 2008) +++ trunk/jaxx-demo/src/main/java/jaxx/demo/tree/DemoTreeHelper.java 2010-06-29 16:09:09 UTC (rev 2009) @@ -25,9 +25,9 @@ package jaxx.demo.tree; -import jaxx.runtime.swing.tree.JaxxTreeHelper; +import jaxx.runtime.swing.nav.JaxxDelegateTreeModel; +import jaxx.runtime.swing.nav.tree.JaxxTreeHelper; -import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeModel; /** @@ -51,11 +51,11 @@ DemoNode root = new DemoNode("jaxxdemo.tree"); // Create model - DefaultTreeModel model = createModel(root); + JaxxDelegateTreeModel<DemoNode> model = createTreeModel(root); // load all nodes of model loadAllNodes(model, root, getDataProvider()); - return model; + return getTreeModel(); } } \ No newline at end of file Modified: trunk/jaxx-demo/src/main/resources/i18n/jaxx-demo-en_GB.properties =================================================================== --- trunk/jaxx-demo/src/main/resources/i18n/jaxx-demo-en_GB.properties 2010-06-29 15:33:32 UTC (rev 2008) +++ trunk/jaxx-demo/src/main/resources/i18n/jaxx-demo-en_GB.properties 2010-06-29 16:09:09 UTC (rev 2009) @@ -195,6 +195,7 @@ jaxxdemo.numbereditor.useFloat= jaxxdemo.numbereditor.useSign= jaxxdemo.title.about=About JAXXDemo... +jaxxdemo.tree.age= jaxxdemo.tree.component.jaxx=JAXX Components jaxxdemo.tree.component.jaxx.editor=Editors jaxxdemo.tree.component.jaxx.tree=Jaxx tree API @@ -209,7 +210,11 @@ jaxxdemo.tree.feature=Features jaxxdemo.tree.feature.databinding=Data Binding jaxxdemo.tree.feature.validation=Validation +jaxxdemo.tree.firstName= jaxxdemo.tree.fun=Fun +jaxxdemo.tree.lastName= +jaxxdemo.tree.tabtitle= +jaxxdemo.treeTable.tabtitle= jaxxdemo.warning.nimbus.landf=Could not init nymbus look and feel, you need at leasr version 1.6u10 of java. jaxxdemo.warning.no.ui=No ui environnement detected model0.f0= Modified: trunk/jaxx-demo/src/main/resources/i18n/jaxx-demo-fr_FR.properties =================================================================== --- trunk/jaxx-demo/src/main/resources/i18n/jaxx-demo-fr_FR.properties 2010-06-29 15:33:32 UTC (rev 2008) +++ trunk/jaxx-demo/src/main/resources/i18n/jaxx-demo-fr_FR.properties 2010-06-29 16:09:09 UTC (rev 2009) @@ -195,6 +195,7 @@ jaxxdemo.numbereditor.useFloat=Utiliser les d\u00E9cimales jaxxdemo.numbereditor.useSign=Utiliser le signe jaxxdemo.title.about=A propos de JAXX Demo... +jaxxdemo.tree.age=Age / ann\u00E9e jaxxdemo.tree.component.jaxx=Composants JAXX jaxxdemo.tree.component.jaxx.editor=Editeurs jaxxdemo.tree.component.jaxx.tree=API JAXX pour les arbres @@ -209,7 +210,11 @@ jaxxdemo.tree.feature=Fonctionnalit\u00E9s jaxxdemo.tree.feature.databinding=Data binding jaxxdemo.tree.feature.validation=Validation +jaxxdemo.tree.firstName=Prenom jaxxdemo.tree.fun=Fun +jaxxdemo.tree.lastName=Nom +jaxxdemo.tree.tabtitle=Arbre +jaxxdemo.treeTable.tabtitle=Arbre tableau jaxxdemo.warning.nimbus.landf=Le look and Feel Nimbus n'a pas \u00E9t\u00E9 trouv\u00E9, il faut au moins la version 1.6u10 de java. jaxxdemo.warning.no.ui=Aucun environnement graphique d\u00E9tect\u00E9 model0.f0= Modified: trunk/jaxx-demo/src/main/resources/log4j.properties =================================================================== --- trunk/jaxx-demo/src/main/resources/log4j.properties 2010-06-29 15:33:32 UTC (rev 2008) +++ trunk/jaxx-demo/src/main/resources/log4j.properties 2010-06-29 16:09:09 UTC (rev 2009) @@ -6,8 +6,8 @@ log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) %M - %m%n log4j.logger.jaxx.demo=INFO -#log4j.logger.jaxx.demo.tree=DEBUG -#log4j.logger.jaxx.runtime.swing.tree=DEBUG +log4j.logger.jaxx.demo.tree=DEBUG +log4j.logger.jaxx.runtime.swing.tree=DEBUG #log4j.logger.jaxx.demo.component.jaxx.tree=DEBUG #log4j.logger.jaxx.demo.config.DemoConfig=DEBUG log4j.logger.jaxx.runtime.swing.editor.config=INFO Modified: trunk/jaxx-runtime/changelog.txt =================================================================== --- trunk/jaxx-runtime/changelog.txt 2010-06-29 15:33:32 UTC (rev 2008) +++ trunk/jaxx-runtime/changelog.txt 2010-06-29 16:09:09 UTC (rev 2009) @@ -33,10 +33,10 @@ - improve classloader managment - keep in DataSource objetCode - fix bug when processDataBinding on a null objectCode - - always clean node cached values when selected it + - always clean demoNode cached values when selected it - add usefull databinding method in Util - * 20081213 [chemit] - improve navigation tree node rendering with some caches + * 20081213 [chemit] - improve navigation tree demoNode rendering with some caches - introduce a ChildBuilder to simplify building of child nodes from a collection or array 0.7 chemit 20081210 @@ -58,7 +58,7 @@ - only enter once in $initialize method in generated code 0.6 chemit 20081117 - * 20081118 [chemit] introduce NavigationUtil, save in context selected node + * 20081118 [chemit] introduce NavigationUtil, save in context selected demoNode * 20081107 [chemit] improve data binding and code generation : - make possible inheritance in binding - add an attribute javaBean to an object : will generate a full java bean support property Copied: trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/DataProvider.java (from rev 2006, trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/tree/DataProvider.java) =================================================================== --- trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/DataProvider.java (rev 0) +++ trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/DataProvider.java 2010-06-29 16:09:09 UTC (rev 2009) @@ -0,0 +1,44 @@ +/* + * #%L + * JAXX :: Runtime + * + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2008 - 2010 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package jaxx.runtime.swing.nav; + +/** + * Contract of provider of data. + * <p/> + * This object is used by {@link JaxxNodeChildLoador} to populate childs of node + * and by {@link jaxx.runtime.swing.nav.tree.AbstractJaxxTreeCellRenderer} to render nodes. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.1 + */ +public interface DataProvider { + + /** + * @return {@code true} is provider is enabled and can provide datas, + * {@code false} otherwise. + */ + boolean isEnabled(); + +} Property changes on: trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/DataProvider.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision HeadURL Added: trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/JaxxDelegateTreeModel.java =================================================================== --- trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/JaxxDelegateTreeModel.java (rev 0) +++ trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/JaxxDelegateTreeModel.java 2010-06-29 16:09:09 UTC (rev 2009) @@ -0,0 +1,313 @@ +/* + * #%L + * JAXX :: Runtime + * + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2008 - 2010 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package jaxx.runtime.swing.nav; + +import jaxx.runtime.swing.nav.tree.JaxxTreeNode; +import jaxx.runtime.swing.nav.treetable.JaxxTreeTableModel; +import jaxx.runtime.swing.nav.treetable.JaxxTreeTableNode; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jdesktop.swingx.treetable.TreeTableNode; + +import javax.swing.event.TreeModelListener; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.MutableTreeNode; +import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.EventListener; +import java.util.List; + +public class JaxxDelegateTreeModel<N extends JaxxNode<N>> { + + /** Logger */ + static private final Log log = LogFactory.getLog(JaxxDelegateTreeModel.class); + + protected DefaultTreeModel treeModel; + protected JaxxTreeTableModel treeTableModel; + + protected boolean isTreeTable; + + public JaxxDelegateTreeModel(DefaultTreeModel treeModel) { + this.treeModel = treeModel; + isTreeTable = false; + } + + public JaxxDelegateTreeModel(JaxxTreeTableModel treeTableModel) { + this.treeTableModel = treeTableModel; + isTreeTable = true; + } + + public boolean isTreeTable() { + return isTreeTable; + } + + @SuppressWarnings({"unchecked"}) + public N getRoot() { + if (!isTreeTable) { + return (N)treeModel.getRoot(); + } + return (N)treeTableModel.getRoot(); + } + + public void setRoot(N node) { + if (!isTreeTable) { + treeModel.setRoot(node); + return; + } + treeTableModel.setRoot((TreeTableNode) node); + } + + @SuppressWarnings({"unchecked"}) + public N getChild(Object parent, int index) { + if (!isTreeTable) { + return (N)treeModel.getChild(parent, index); + } + return (N)treeTableModel.getChild(parent, index); + } + + public int getChildCount(Object parent) { + if (!isTreeTable) { + return treeModel.getChildCount(parent); + } + return treeTableModel.getChildCount(parent); + } + + public boolean isLeaf(Object node) { + if (!isTreeTable) { + return treeModel.isLeaf(node); + } + return treeTableModel.isLeaf(node); + } + + public void valueForPathChanged(TreePath path, Object newValue) { + if (!isTreeTable) { + treeModel.valueForPathChanged(path, newValue); + return; + } + treeTableModel.valueForPathChanged(path, newValue); + } + + public int getIndexOfChild(Object parent, Object child) { + if (!isTreeTable) { + return treeModel.getIndexOfChild(parent, child); + } + return treeTableModel.getIndexOfChild(parent, child); + } + + public void addTreeModelListener(TreeModelListener l) { + if (!isTreeTable) { + treeModel.addTreeModelListener(l); + return; + } + treeTableModel.addTreeModelListener(l); + } + + public void removeTreeModelListener(TreeModelListener l) { + if (!isTreeTable) { + treeModel.removeTreeModelListener(l); + return; + } + treeTableModel.removeTreeModelListener(l); + } + + public void nodesWereInserted(N parent, int[] indices) { + if (!isTreeTable) { + treeModel.nodesWereInserted(parent, indices); + return; + } + nodeStructureChanged(parent); + } + + public void nodeWereInserted(N parentNode, int childIndice, N node) { + if (!isTreeTable) { + int[] indices = new int[1]; + indices[0] = childIndice; + treeModel.nodesWereInserted(parentNode, indices); + return; + } + + nodeStructureChanged(node); + } + + public void insertNodeInto(N newChild, + N parent, int index){ + if (!isTreeTable) { + treeModel.insertNodeInto((MutableTreeNode)newChild, (MutableTreeNode)parent, index); + return; + } + treeTableModel.nodeStructureChanged((JaxxTreeTableNode<?>) parent); + } + + public void removeNodeFromParent(N node) { + if (!isTreeTable) { + treeModel.removeNodeFromParent((MutableTreeNode)node); + return; + } + treeTableModel.nodeStructureChanged((JaxxTreeTableNode<?>) node); + } + + public void nodeChanged(TreeNode node) { + if (!isTreeTable) { + treeModel.nodeChanged(node); + return; + } + treeTableModel.nodeChanged((JaxxTreeTableNode<?>) node); + } + + public void reload(N node) { + if (!isTreeTable) { + treeModel.reload(); + return; + } + treeTableModel.getModelSupport().fireTreeStructureChanged(new TreePath(getPathToRoot(node))); + } + + public void nodesWereRemoved(TreeNode node, int[] childIndices, + Object[] removedChildren) { + if (!isTreeTable) { + treeModel.nodesWereRemoved(node, childIndices, removedChildren); + return; + } + treeTableModel.getModelSupport().fireChildrenRemoved(new TreePath(getPathToRoot(node)), childIndices, removedChildren); + } + + public void nodesChanged(TreeNode node, int[] childIndices) { + if (!isTreeTable) { + treeModel.nodesChanged(node, childIndices); + return; + } + treeTableModel.nodeChanged((JaxxTreeTableNode<?>) node); + } + + public void nodeStructureChanged(TreeNode node) { + if (!isTreeTable) { + treeModel.nodeStructureChanged(node); + return; + } + treeTableModel.nodeStructureChanged((JaxxTreeTableNode<?>) node); + } + + public TreeNode[] getPathToRoot(TreeNode aNode) { + if (!isTreeTable) { + return treeModel.getPathToRoot(aNode); + } + return treeTableModel.getPathToRoot((TreeTableNode)aNode); + } + + public TreeModelListener[] getTreeModelListeners() { + if (!isTreeTable) { + return treeModel.getTreeModelListeners(); + } + return treeTableModel.getModelSupport().getTreeModelListeners(); + } + + @SuppressWarnings({"unchecked"}) + public <T extends EventListener> T[] getListeners(Class<T> listenerType) { + if (!isTreeTable) { + return treeModel.getListeners(listenerType); + } + TreeModelListener[] treeModelListeners = getTreeModelListeners(); + List<TreeModelListener> result = new ArrayList<TreeModelListener>(); + for (TreeModelListener listener : treeModelListeners) { + if (listener.getClass().isAssignableFrom(listenerType)) { + result.add(listener); + } + } + return (T[])result.toArray(); + } + + /** + * Notifies that all childs nodes of {@code node} were + * inserted. + * <p/> + * <b>Note:</b> The method recurses on childs (always notify parent before child) + * + * @param node node where all childs where inserted + */ + @SuppressWarnings({"unchecked"}) + public void notifyChildNodesInserted(N node) { + if (isTreeTable) { + nodeStructureChanged(node); + return; + } + int count = node.getChildCount(); + if (count < 1) { + if (log.isDebugEnabled()) { + log.debug("Skip for leaf node : " + node); + } + return; + } + if (log.isDebugEnabled()) { + log.debug("Notify for node : " + node + ", " + count + " child(s) inserted."); + } + int[] indices = new int[count]; + for (int i = 0; i < count; i++) { + indices[i] = i; + } + + nodesWereInserted(node, indices); + + // recurse notify on childs + for (Enumeration<? extends JaxxTreeNode<?>> childs = node.children(); + childs.hasMoreElements();) { + N child = (N) childs.nextElement(); + notifyChildNodesInserted(child); + } + } + + /** + * Notifies that the {@code node} was inserted. + * <p/> + * <b>Note:</b> The method recurses on childs (always notify parent before child) + * + * @param node node inserted + */ + public void notifyNodeInserted(N node) { + if (isTreeTable) { + nodeStructureChanged(node); + return; + } + + N parent = node.getParent(); + if (parent != null) { + int indice = parent.getIndex(node); + if (log.isDebugEnabled()) { + log.debug("Notify for node : " + node + ", for parent [" + parent + "] child " + indice + " inserted."); + } + nodesWereInserted(parent, new int[]{indice}); + notifyChildNodesInserted(node); + } + } + + public DefaultTreeModel getTreeDelegateModel() { + return treeModel; + } + + public JaxxTreeTableModel getTreeTableDelegateModel() { + return treeTableModel; + } +} \ No newline at end of file Copied: trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/JaxxNode.java (from rev 2006, trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/tree/JaxxNode.java) =================================================================== --- trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/JaxxNode.java (rev 0) +++ trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/JaxxNode.java 2010-06-29 16:09:09 UTC (rev 2009) @@ -0,0 +1,206 @@ +/* + * #%L + * JAXX :: Runtime + * + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2008 - 2010 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package jaxx.runtime.swing.nav; + +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeNode; +import java.io.Serializable; + +/** + * Definition of a node with a optional {@link #childLoador} to build childs of + * node. + * <p/> + * A node is identified by an {@link #id} of an associated data of type + * {@link #internalClass}. + * <p/> + * <b>Note:</b> + * <p><i> While using a {@link #childLoador}, we can not know before node + * was loaded the exact count of his childs. As a matter of facts, real leaf + * nodes appears at the beginning in ui as a not leaf (there is a root handler). + * When node was loaded, a leaf node will be then displayed as required. + * </i></p> + * <p/> + * <h2>Why JaxxNode is generic ?</h2> + * In a project, you should implements your own Node extending with one like this : + * <pre> + * class MyNode extends JaxxNode<MyNode> { ... } + * </pre> + * While in this class, you overrides every method with a node return type, + * co-variance you'll be able to use this code : + * <pre> + * MyNode parentNode = new MyNode(); + * MyNode node = parentNode.getFirstNode(); + * </pre> + * So for final application this generic type avoid any cast for your own node + * type, this is quite convinient. + * <p/> + * Even if in your project, you wants to have a heriarchy of nodes, this will + * still works (if you use a genercic type on your abstract nodes). + * <h2>Internal states</h2> + * <ul> + * <li><b>internalClass</b> : the type of data associated with the node</li> + * <li><b>context</b> : an optinal context to distinguish different types of + * node with same {@code internalclass}</li> + * <li><b>id</b> : id of the data associated with the node</li> + * <li><b>dirty</b> : flag sets to {@code true} when node render MUST be recomputed</li> + * <li><b>loaded</b> : flag sets to {@code true} when node was loaded</li> + * <li><b>childLoador</b> : optional loador of childs</li> + * </ul> + * <h2>Static nodes</h2> + * Some nodes do not need auto-loading, we call them {@code static nodes}. + * The method {@link #isStaticNode()} gives this state. + * <p/> + * <b>Note:</b> A static node has no {@link #childLoador}. + * <h2>Node loading</h2> + * Initialy node has no data child nodes, ({@link #isLoaded()} equals + * {@code false}). + * when model requires node's childs, it can load them via method + * {@link #populateNode(JaxxDelegateTreeModel , DataProvider, boolean)} + * and {@link #populateChilds(JaxxDelegateTreeModel , DataProvider)} methods. + * <h2>Node rendering</h2 + * the {@link jaxx.runtime.swing.nav.tree.AbstractJaxxTreeCellRenderer} looks the {@link #dirty} state to + * know when render should be (re-)compute and set back the state to {@code false}. + * <p/> + * Each time, a node is modified, the {@link #dirty} should be set to {@code true}. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.1 + */ +public interface JaxxNode <N extends JaxxNode<N>> extends Cloneable, TreeNode, Serializable { + + String getId(); + + String getContext(); + + Class<?> getInternalClass(); + + boolean isLoaded(); + + boolean isDirty(); + + /** + * Convinient method to known if the node is a {@code String} typed. + * + * @return {@code true} if the type of node if + */ + boolean isStringNode(); + + /** + * To know if the node is static. + * <p/> + * A {@code static} node has no {@link #childLoador}. + * + * @return {@code true} when the node is static : says, the node has + * no {@link #childLoador}. + */ + boolean isStaticNode(); + + /** + * Gets the first node form this one to the root which has a none + * {@code String} type. + * + * @return the first concrete node type + */ + N getContainerNode(); + + /** + * Given an {@code id}, obtain the child with matching id. + * <p/> + * If node is NOT {@code loaded}, then first loads it (method + * {@link #populateChilds(DefaultTreeModel, DataProvider)}) then do search + * on direct childs of the node. + * + * @param id the id of the researched node + * @param model model owner of nodes + * @param provider data provider + * @return the found node or {@code null} if not found + */ + N findNodeById(String id, + JaxxDelegateTreeModel<N> model, + DataProvider provider); + + /** + * Changes the {@link #dirty} state. + * <p/> + * As a side effect, when a renderer will use this node, it will force to + * reload the render from the {@link DataProvider}. + * + * @param dirty the new dirty value + */ + void setDirty(boolean dirty); + + @Override + boolean isLeaf(); + + Object getUserObject(); + + @Override + String toString(); + + //-------------------------------------------------------------------------- + //-- Populate methods + //-------------------------------------------------------------------------- + + /** + * To populate the node. A side-effect of this method is to set {@code dirty} + * the node (renderer will recompute the render of the node). + * <p/> + * If {@code populateChilds} is set to {@code true}, then also populate + * childs of the node using the given {@code dataProvider}. + * + * @param model le delegate modèles content le noeud + * @param provider le provider de données + * @param populateChilds un drapeau pour charger aussi les fils du noeud courant + */ + void populateNode(JaxxDelegateTreeModel<N> model, + DataProvider provider, + boolean populateChilds); + + /** + * To populate childs of the node (only when a none static node). + * A side-effect of this method is to set {@code loaded} of the node. + * <p/> + * For a static node, do nothing. + * + * @param model model owner of the node + * @param provider data provider + */ + void populateChilds(JaxxDelegateTreeModel<N> model, DataProvider provider); + + //-------------------------------------------------------------------------- + //-- Overrides to use generic type as return + //-------------------------------------------------------------------------- + + boolean isRoot(); + + @Override + N getParent(); + + void add(N node); + + void remove(N node); + + void insert(N node, int position); +} Property changes on: trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/JaxxNode.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision HeadURL Copied: trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/JaxxNodeChildLoador.java (from rev 2006, trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/tree/JaxxNodeChildLoador.java) =================================================================== --- trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/JaxxNodeChildLoador.java (rev 0) +++ trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/JaxxNodeChildLoador.java 2010-06-29 16:09:09 UTC (rev 2009) @@ -0,0 +1,168 @@ +/* + * #%L + * JAXX :: Runtime + * + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2008 - 2010 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package jaxx.runtime.swing.nav; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.Serializable; +import java.util.List; + +/** + * Object to load childs of a node. + * <p/> + * It uses {@link DataProvider} in method + * {@link #loadChilds(JaxxDelegateTreeModel , JaxxNode, DataProvider)} to obtain datas + * then build childs nodes. + * <p/> + * A factory of such objects can be found in {@link jaxx.runtime.swing.nav.tree.JaxxTreeHelper} to make + * them reusable in other places than inside a {@link JaxxNode} to auto-load + * childs. + * <p/> + * For example when you want to creat by hand a new node, always prefer to reuse + * a such object rathen than duplicate same code in helper... + * + * @author tchemit <chemit@codelutin.com> + * @param <T> type of data used to create nodes (can be just a String type to use only ids) + * @param <O> type of data associated with nodes + * @param <N> type of node to used (to make possible full co-variance and no cast in fal implementations). + * @see jaxx.runtime.swing.nav.tree.JaxxTreeHelper + * @see JaxxNode + * @since 2.1 + */ +public abstract class JaxxNodeChildLoador<T, O, N extends JaxxNode<N>> implements Serializable { + + /** Logger */ + static private final Log log = LogFactory.getLog(JaxxNodeChildLoador.class); + + /** Type of data of the node */ + protected final Class<O> beanType; + + protected JaxxNodeChildLoador(Class<O> beanType) { + this.beanType = beanType; + } + + /** + * Obtain the list of data used to create nodes. + * <p/> + * If type {@code T} is {@code O}, we directly use the data associated with nodes. + * + * @param parentClass type of parent + * @param parentId id of parent + * @param dataProvider the data provider + * @return the list of data + * @throws Exception if any problem + */ + public abstract List<T> getData(Class<?> parentClass, + String parentId, + DataProvider dataProvider) throws Exception; + + /** + * Hook to create a child node given his {@code data}. + * + * @param data the data of the node to create + * @param dataProvider the data provider + * @return the created node + */ + public abstract N createNode(T data, DataProvider dataProvider); + + /** + * Returns the type of data associated with nodes to create. + * + * @return the type of data associated with created nodes. + */ + public Class<O> getBeanType() { + return beanType; + } + + /** + * Load childs of the given {@code parentnode}. + * + * @param model the model owner of nodes + * @param parentNode the parent node where to insert nodes + * @param dataProvider data provider + * @throws Exception pour tout probleme de recuperation de donnees + */ + public void loadChilds(JaxxDelegateTreeModel<N> model, + N parentNode, + DataProvider dataProvider) throws Exception { + + N containerNode = parentNode.getContainerNode(); + + List<T> datas; + if (containerNode == null) { + + // pas d'ancetre, il doit s'agir d'un premier noeud de données + // depuis le noeud root + + // recuperation des objets fils (sans connaitre de parent) + datas = getData(null, null, dataProvider); + + } else { + if (log.isDebugEnabled()) { + log.debug("search data for " + containerNode.getInternalClass() + + " : " + containerNode.getId()); + } + + // recuperation des objets fils + datas = getData(containerNode.getInternalClass(), + containerNode.getId(), + dataProvider); + } + + // on charge les fils + addChildNodes(parentNode, datas, dataProvider); + + // notifie le modele d'un ajout de noeuds + model.notifyChildNodesInserted(parentNode); + } + + /** + * Add childs to given {@code parentNode} using retrive {@code datas} from + * the data provider. + * <p/> + * This method is invoked by the {@link #loadChilds(JaxxDelegateTreeModel , JaxxNode, DataProvider)}. + * + * @param parentNode the node where to insert + * @param datas the data used to create node + * @param dataProvider the data provider + */ + protected void addChildNodes(N parentNode, + List<T> datas, + DataProvider dataProvider) { + + // creation des noeuds fils + if (datas != null) { + for (T o : datas) { + if (log.isInfoEnabled()) { + log.info("[" + parentNode + "] Will add child node for " + o); + } + N node = createNode(o, dataProvider); + parentNode.add(node); + } + } + } + +} \ No newline at end of file Property changes on: trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/JaxxNodeChildLoador.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision HeadURL Copied: trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/package.html (from rev 2006, trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/tree/package.html) =================================================================== --- trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/package.html (rev 0) +++ trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/package.html 2010-06-29 16:09:09 UTC (rev 2009) @@ -0,0 +1,50 @@ +<html> +<body> +<h1>JAXX - tree utilities</h1> + +This package contains all the classes of the new tree framework. + +<p> + Replace the previous framework from package + <code>jaxx.runtime.swing.navigation</code> +</p> + +<h1>Why this api ?</h1> + +<p> + The main goal of this api is to offer an auto-loading system of tree + model. +</p> + +<p> + While previous api we had to load all the model in memory, now we can build + a + tree model with no data. +</p> + +<p> + When the tree will need to expand a node, it will ask first in childs of + node + were loaded, if not, load them then give by hand to system. +</p> + +<h1>Api</h1> + +<h2>DataProvider</h2> +Contract of objet responsible of acquiring data to populate nodes and render them. + +<h2>JaxxNode</h2> +An override of DefaultMutableTreeNode customized for our purpose (loaded, dirty +states,...) + +<h2>JaxxNodeChildLoador</h2> +Object to load childs of a node using DataProvider. + +<h2>JaxxTreeHelper</h2> +Helper to manage a tree using auto-loading nodes. + +<h2>AbstractJaxxTreeCellRenderer</h2> +Abstract renderer using DataProvider to acquire node render. + +</body> +</html> Property changes on: trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/package.html ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision HeadURL Copied: trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/tree/AbstractJaxxTreeCellRenderer.java (from rev 2006, trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/tree/AbstractJaxxTreeCellRenderer.java) =================================================================== --- trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/tree/AbstractJaxxTreeCellRenderer.java (rev 0) +++ trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/tree/AbstractJaxxTreeCellRenderer.java 2010-06-29 16:09:09 UTC (rev 2009) @@ -0,0 +1,128 @@ +/* + * #%L + * JAXX :: Runtime + * + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2008 - 2010 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package jaxx.runtime.swing.nav.tree; + +import jaxx.runtime.swing.nav.DataProvider; +import jaxx.runtime.swing.nav.JaxxNode; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.swing.tree.DefaultTreeCellRenderer; +import java.util.HashMap; +import java.util.Map; + +import static org.nuiton.i18n.I18n._; + +/** + * Le renderer abstrait (qui a toutes les methodes qui aident) pour implanter de + * vrai renderer pour les différents cas d'utilisation de l'abre de navigation. + * + * @author chemit <chemit@codelutin.com> + * @since 1.2 + */ +public abstract class AbstractJaxxTreeCellRenderer<N extends JaxxNode<N>> extends DefaultTreeCellRenderer { + + /** Logger */ + protected static final Log log = + LogFactory.getLog(AbstractJaxxTreeCellRenderer.class); + + /** source de donnée */ + protected DataProvider dataProvider; + + /** le cache de rendu */ + protected final Map<N, String> renderCache = new HashMap<N, String>(); + + /** + * Determines the text render of a node using the {@link #dataProvider}. + * + * @param node the node to render + * @return the text render of the node + */ + protected abstract String computeNodeText(N node); + + protected AbstractJaxxTreeCellRenderer() { + } + + public DataProvider getDataProvider() { + return dataProvider; + } + + public void setDataProvider(DataProvider dataProvider) { + this.dataProvider = dataProvider; + + // une nouvelle source utilisée, on vide le cache + clearCache(); + } + + public void clearCache() { + renderCache.clear(); + } + + public void invalidateCache(N node) { + renderCache.remove(node); + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + clearCache(); + } + + public String getNodeText(N node) { + if (node == null) { + return null; + } + String text; + + if (node.isDirty() || !renderCache.containsKey(node)) { + + // calculer le rendu du noeud + if (node.isStringNode()) { + text = _(node.getId()); + + } else { + + text = computeNodeText(node); + } + + if (log.isDebugEnabled()) { + log.debug("text for node [" + node + "] = <" + text + ">"); + } + + // sauvegarde dans le cache + renderCache.put(node, text); + + // le noeud est de nouveau propre + node.setDirty(false); + + } else { + + // recupération directement du rendu dans le cache + text = renderCache.get(node); + } + + return text; + } +} Property changes on: trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/tree/AbstractJaxxTreeCellRenderer.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision HeadURL Copied: trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/tree/JaxxTreeHelper.java (from rev 2006, trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/tree/JaxxTreeHelper.java) =================================================================== --- trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/tree/JaxxTreeHelper.java (rev 0) +++ trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/tree/JaxxTreeHelper.java 2010-06-29 16:09:09 UTC (rev 2009) @@ -0,0 +1,989 @@ +/* + * #%L + * JAXX :: Runtime + * + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2008 - 2010 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package jaxx.runtime.swing.nav.tree; + +import jaxx.runtime.swing.nav.JaxxDelegateTreeModel; +import jaxx.runtime.swing.nav.treetable.JaxxTreeTableModel; +import jaxx.runtime.swing.nav.DataProvider; +import jaxx.runtime.swing.nav.JaxxNode; +import jaxx.runtime.swing.nav.JaxxNodeChildLoador; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jdesktop.swingx.JXTreeTable; + +import javax.swing.JTree; +import javax.swing.event.TreeExpansionEvent; +import javax.swing.event.TreeModelEvent; +import javax.swing.event.TreeModelListener; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; +import javax.swing.event.TreeWillExpandListener; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeCellRenderer; +import javax.swing.tree.TreePath; +import javax.swing.tree.TreeSelectionModel; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Tree helper to deal with the build of trees and other usefull operations. + * <p/> + * A helper acts as an handler for a {@code tree}. It owns the {@link #model} of + * the {@link #tree}. + * <p/> + * <b>Note:</b> A helper can NOT be used to manage multi-trees. + * <h2>Internal states</h2 + * <h3>Internal model</h3> + * To create the model, use method {@link #createModel(jaxx.runtime.swing.nav.JaxxNode)} given a + * root node. + * <p/> + * To obtain the model, use method {@link #getModel()}. + * <p/> + * <b>Note:</b> The helper internal model can be different from the tree model, + * but must be the <b>lowest</b> model, other models must listen nicely this + * model to obtain model modification and selection notifications. + * <h3>Internal tree</h3> + * As said before, a helper matches exactly one tree. + * <p/> + * To register the tree, use method {@link #setTree(JTree, boolean, TreeSelectionListener)}. + * <p/> + * To obtain the tree, use method {@link #getTree()}. + * <h3>Internal data provider</h3> + * To populate childs nodes and render nodes, we use a {@link jaxx.runtime.swing.nav.DataProvider}. + * <p/> + * To register the data provider, use method {@link #setDataProvider(jaxx.runtime.swing.nav.DataProvider)}. + * <p/> + * To obtain the data provider, use method {@link #getDataProvider()}. + * <h2>Internal listeners</h2> + * Several listeners are used to manage the auto-loading of nodes in model : + * <h3>{@link #expandListener}</h3> + * This listener will load node's childs before node expands if the node is not loaded. + * <p/> + * See the {@link jaxx.runtime.swing.nav.JaxxNode#isLoaded()} method. + * <h3>{@link #treeModelListener}</h3> + * To listen modification of the model, it will mainly repopulate nodes when + * required. + * <p/> + * See the method {@link #populateNode(jaxx.runtime.swing.nav.JaxxNode , Object[], boolean)}. + * <h3>{@link #selectionListener}</h3> + * To listen modification of the selection, it will mainly expand paths if required. + * <p/> + * This is a requirement, since childs of a node should NOT be loaded, so when + * selects a node, always check the path from root to selected node are all fully + * loaded. + * <h2>Model methods</h2> + * The helper offers some methods to modify and query the internal tree model. + * <h3>Model modification</h3> + * <ul> + * <li>{@link #createModel(jaxx.runtime.swing.nav.JaxxNode)}</li> + * <li>{@link #insertNode(jaxx.runtime.swing.nav.JaxxNode , jaxx.runtime.swing.nav.JaxxNode)}</li> + * <li>{@link #removeNode(jaxx.runtime.swing.nav.JaxxNode)}</li> + * <li>{@link #moveNode(jaxx.runtime.swing.nav.JaxxNode , jaxx.runtime.swing.nav.JaxxNode , int)}</li> + * <li>{@link #refreshNode(jaxx.runtime.swing.nav.JaxxNode , boolean)}</li> + * <li>{@link #loadAllNodes(jaxx.runtime.swing.nav.JaxxDelegateTreeModel , jaxx.runtime.swing.nav.JaxxNode , jaxx.runtime.swing.nav.DataProvider)}</li> + * <p/> + * </ul> + * <h3>Model selection modification</h3> + * <ul> + * <li>{@link #selectNode(jaxx.runtime.swing.nav.JaxxNode)}</li> + * <li>{@link #selectNode(String...)}</li> + * <li>{@link #selectParentNode()}</li> + * </ul> + * <h3>Model query</h3> + * <ul> + * <li>{@link #findNode(jaxx.runtime.swing.nav.JaxxNode , String...)}</li> + * </ul> + * <h3>Child loadors factory</h3> + * The class offers a factory of {@link jaxx.runtime.swing.nav.JaxxNodeChildLoador}, use the method + * {@link #getChildLoador(Class)} to obtain the correct child loador given his type. + * + * @author tchemit <chemit@codelutin.com> + * @see jaxx.runtime.swing.nav.JaxxNode + * @see jaxx.runtime.swing.nav.JaxxNodeChildLoador + * @see AbstractJaxxTreeCellRenderer + * @since 2.1 + */ +public class JaxxTreeHelper<N extends JaxxNode<N>> { + + /** Logger */ + static private final Log log = LogFactory.getLog(JaxxTreeHelper.class); + + /** + * The shared instance of tree model. + * <p/> + * A helper deals with only ONE model (this one), becuase we add some + * listeners on it, we prefer always to keep ONE instance (any way this is + * a good thing). + * <p/> + * If you want to create a new model, just creates the good root node and + * push it in this model. + * <p/> + * <b>Note:</b> The model of the registred {@link #tree} can be different + * from this one. + * <p/> + * For example, if you wrap the shared model with a filter model... Anyway, all + * listeners of this helper apply always of THIs model. + */ + protected JaxxDelegateTreeModel<N> model; + + /** + * The shared instance of tree. + * <p/> + * A helper deleas with only ONE tree (this one), becuase we add some listeners + * on it, we prefer always to kepp ONE safe instance. + * <p/> + * If you need to work with more than one helper, please instanciat a new + * helper for each tree. + */ + protected JTree tree; + + /** + * The shared instance of treeTable. + * <p/> + * A helper deleas with only ONE tree (this one), becuase we add some listeners + * on it, we prefer always to kepp ONE safe instance. + * <p/> + * If you need to work with more than one helper, please instanciat a new + * helper for each tree. + */ + protected JXTreeTable treeTable; + + /** The shared data provider used to obtain datas to populate nodes and render them. */ + protected DataProvider dataProvider; + + /** + * A {@link TreeWillExpandListener} used to listen when tree should expand. + * <p/> + * If so, the listener will load selected node childs if required + * (says when the {@link jaxx.runtime.swing.nav.JaxxNode#isLoaded()} is sets to {@code false}). + */ + protected TreeWillExpandListener expandListener; + + /** + * pour ouvrir les fils d'un noeud que l'on vient de sélectionner pour + * éviter d'avoir à faire des doubles clics. + */ + protected TreeSelectionListener selectionListener; + + /** + * pour recharger le rendu des noeuds (et charger les fils si nécessaires) + * lors d'une modification dans le modèle de l'arbre. + */ + protected TreeModelListener treeModelListener; + + /** Cache of child loadors. */ + protected static Set<? super JaxxNodeChildLoador<?, ?, ?>> childLoadors; + + protected static Set<? super JaxxNodeChildLoador<?, ?, ?>> getChildLoadors() { + if (childLoadors == null) { + childLoadors = new HashSet<JaxxNodeChildLoador<?, ?, ?>>(); + } + return childLoadors; + } + + /** + * Obtains the {@link JaxxNodeChildLoador} of the given {@code type} from + * internal cache. + * <p/> + * <b>Note:</b> The loador will be instanciated if not found, and push in cache. + * + * @param type the type of loador to get + * @param <L> the type of loador to get + * @return the loador from cache + */ + @SuppressWarnings({"unchecked"}) + public static <L extends JaxxNodeChildLoador<?, ?, ?>> L getChildLoador(Class<L> type) { + Set<? super JaxxNodeChildLoador<?, ?, ?>> cache = getChildLoadors(); + JaxxNodeChildLoador<?, ?, ?> result = null; + for (Object loador : cache) { + if (type.equals(loador.getClass())) { + result = (JaxxNodeChildLoador<?, ?, ?>) loador; + break; + } + } + if (result == null) { + // add it in cache + try { + result = type.newInstance(); + cache.add(result); + if (log.isDebugEnabled()) { + log.debug("Add " + result + " in loadors cache (new size:" + cache.size() + ")."); + } + } catch (Exception e) { + throw new IllegalArgumentException("Could not instanciate loador [" + type.getName() + "]", e); + } + } + return (L) result; + } + + public JaxxTreeHelper() { + + selectionListener = new TreeSelectionListener() { + + @Override + public void valueChanged(TreeSelectionEvent e) { + if (!checkModel()) { + return; + } + + // Hack, because event.getSource for TreeTable doesnt return selectionModel + TreeSelectionModel source = getSelectionModel(); + + if (source.isSelectionEmpty()) { + + // empty selection + if (log.isDebugEnabled()) { + log.debug("Selection is empty."); + } + return; + } + + boolean debugEnabled = log.isDebugEnabled(); + boolean traceEnabled = log.isTraceEnabled(); + for (TreePath path : e.getPaths()) { + + N node = getNode(path); + if (node == null) { + + // pas de noeud selectionne + if (debugEnabled) { + log.debug("Skip for null node."); + } + continue; + } + + boolean isAdded = e.isAddedPath(path); + + TreePath pathToExpand = new TreePath(model.getPathToRoot(node)); + boolean pathExpanded = isExpanded(pathToExpand); + + if (traceEnabled || isAdded && debugEnabled) { + log.debug("==== Node selection ===================================="); + log.debug("node ? " + node); + log.debug("is added ? " + isAdded); + log.debug("is path expanded ? " + pathExpanded); + log.debug("is node static ? " + node.isStaticNode()); + log.debug("is node loaded ? " + node.isLoaded()); + log.debug("is node leaf ? " + node.isLeaf()); + log.debug("node nb childs ? " + node.getChildCount()); + } + + if (isAdded && !pathExpanded) { + + // ask to expand path + log.info("expand node [" + pathToExpand + "]"); + expandPath(pathToExpand); + } + } + } + }; + expandListener = new TreeWillExpandListener() { + @Override + public void treeWillExpand(TreeExpansionEvent event) { + + if (!checkModel()) { + // no model + return; + } + + N source = getNode(event.getPath()); + + if (source.isLoaded()) { + + // node is already loaded, nothing to do + return; + } + + if (log.isDebugEnabled()) { + log.debug("will load childs of node [" + source + "]"); + } + // populate childs of node + source.populateChilds(model, getDataProvider()); + } + + @Override + public void treeWillCollapse(TreeExpansionEvent event) { + } + }; + + + treeModelListener = new TreeModelListener() { + @Override + public void treeNodesInserted(TreeModelEvent e) { + if (!checkModel()) { + // no model + return; + } + N source = getNode(e.getTreePath()); + Object[] children = e.getChildren(); + if (log.isDebugEnabled()) { + log.debug(getMessage("inserted ", source, children)); + } + + // ask to populate children nodes + populateNode(null, children, false); + } + + @SuppressWarnings({"unchecked"}) + @Override + public void treeNodesRemoved(TreeModelEvent e) { + if (!checkModel()) { + // no model + return; + } + N source = getNode(e.getTreePath()); + Object[] children = e.getChildren(); + if (log.isDebugEnabled()) { + log.debug(getMessage("removed ", source, children)); + } + + // Invalidates nodes in renderer cache (if any) + AbstractJaxxTreeCellRenderer<N> renderer = getTreeCellRenderer(); + if (children != null && renderer != null) { + for (Object child : children) { + renderer.invalidateCache((N) child); + } + } + } + + @Override + public void treeNodesChanged(TreeModelEvent e) { + if (!checkModel()) { + // no model + return; + } + + N source = getNode(e.getTreePath()); + Object[] children = e.getChildren(); + if (log.isDebugEnabled()) { + log.debug(getMessage("changed ", source, children)); + } + + // ask to populate modified child nodes + populateNode(null, children, false); + } + + @Override + public void treeStructureChanged(TreeModelEvent e) { + if (!checkModel()) { + // no model + return; + } + N source = getNode(e.getTreePath()); + Object[] children = e.getChildren(); + if (log.isDebugEnabled()) { + log.debug(getMessage("structure changed", source, children)); + } + + // ask to populate structure modified node and nodes recursively + populateNode(source, children, true); + } + + protected String getMessage(String action, N source, Object[] children) { + StringBuilder sb = new StringBuilder(); + sb.append("==== Nodes "); + sb.append(action); + sb.append(" ================="); + sb.append("\nsource : ").append(source); + sb.append("\nnb nodes : "); + sb.append(children == null ? 0 : children.length); + if (children != null) { + int i = 0; + for (Object child : children) { + sb.append("\n ["); + sb.append(i++); + sb.append("] - "); + sb.append(child); + } + } + return sb.toString(); + } + }; + } + + /** + * Obtains the attached data provider used to populate and render nodes. + * + * @return the attached data provider + */ + protected DataProvider getDataProvider() { + return dataProvider; + } + + /** + * Obtains the registred tree. + * + * @return the registred tree for this helper or {@code null} if none was registred + */ + public JTree getTree() { + return tree; + } + + /** + * Obtains the registred tree table. + * + * @return the registred tree table for this helper or {@code null} if none was registred + */ + public JXTreeTable getTreeTable() { + return treeTable; + } + + /** + * Obtains the jaxx delegate tree model + * + * @return the internal tree model or {@code null} if none was created. + */ + public JaxxDelegateTreeModel<N> getModel() { + return model; + } + + /** + * Obtains the internal tree table model. + * + * @return the internal tree table model or {@code null} if none was created. + */ + public DefaultTreeModel getTreeModel() { + return model.getTreeDelegateModel(); + } + + /** + * Obtains the internal tree table model. + * + * @return the internal tree table model or {@code null} if none was created. + */ + public JaxxTreeTableModel getTreeTableModel() { + return model.getTreeTableDelegateModel(); + } + + public N getRootNode() { + if (!checkModel()) { + return null; + } + return model.getRoot(); + } + + /** + * Obtains the {@link AbstractJaxxTreeCellRenderer} renderer of the + * registred tree. + * + * @return the renderer of the registred tree or null if no tree was + * registred nor the renderer is a {@link AbstractJaxxTreeCellRenderer}. + */ + @SuppressWarnings({"unchecked"}) + public AbstractJaxxTreeCellRenderer<N> getTreeCellRenderer() { + JTree t = getTree(); + if (t == null) { + return null; + } + TreeCellRenderer r = t.getCellRenderer(); + if (r instanceof AbstractJaxxTreeCellRenderer<?>) { + return (AbstractJaxxTreeCellRenderer<N>) r; + } + return null; + } + + /** + * Obtains the selected node of the registred tree. + * + * @return the selected tree or {@code null} if no registred tree nor + * selection empty. + */ + public N getSelectedNode() { + JTree tree = getTree(); + if (tree == null) { + return null; + } + TreePath path = tree.getSelectionPath(); + N node = null; + if (path != null) { + node = getNode(path); + } + return node; + } + + /** + * Obtains the path of ids fro the root node to the selected node on the + * registred tree. + * + * @return the array of ids from root node to selected node. + */ + public String[] getSelectedIds() { + List<String> result = new ArrayList<String>(); + N selectedNode = getSelectedNode(); + while (selectedNode != null && !selectedNode.isRoot()) { + + result.add(selectedNode.getId()); + selectedNode = selectedNode.getParent(); + } + Collections.reverse(result); + return result.toArray(new String[result.size()]); + } + + /** + * Registers the {@code dataProvider} for the helper. + * <p/> + * <b>Node:</b> As a side-effect, the provider will be propagate to the + * renderer of the registred tree (if any). + * + * @param dataProvider the data provider to use + */ + public void setDataProvider(DataProvider dataProvider) { + this.dataProvider = dataProvider; + AbstractJaxxTreeCellRenderer<N> renderer = getTreeCellRenderer(); + if (renderer != null) { + + // dispatch provider to renderer + renderer.setDataProvider(dataProvider); + } + } + + /** + * Registers the given {@code tree} for this helper. + * <p/> + * <b>Note:</b> as a side-effect, it will register (if required) the + * {@link #expandListener} listener and the {@link #selectionListener}. + * + * @param tree the tree to register + * @param addExpandTreeListener a flag to add expand listener + * @param listener the optional selection listener to add + */ + public void setTree(JTree tree, + boolean addExpandTreeListener, + TreeSelectionListener listener) { + this.tree = tree; + if (addExpandTreeListener) { + this.tree.addTreeWillExpandListener(expandListener); + } + if (listener != null) { + this.tree.getSelectionModel().addTreeSelectionListener(listener); + } + this.tree.getSelectionModel().addTreeSelectionListener(selectionListener); + } + + /** + * Registers the given {@code treeTable} for this helper. + * <p/> + * <b>Note:</b> as a side-effect, it will register (if required) the + * {@link #expandListener} listener and the {@link #selectionListener}. + * + * @param treeTable the tree table to register + * @param addExpandTreeListener a flag to add expand listener + * @param listener the optional selection listener to add + */ + public void setTreeTable(JXTreeTable treeTable, + boolean addExpandTreeListener, + TreeSelectionListener listener) { + this.treeTable = treeTable; + if (addExpandTreeListener) { + this.treeTable.addTreeWillExpandListener(expandListener); + } + if (listener != null) { + this.treeTable.addTreeSelectionListener(listener); + } + this.treeTable.addTreeSelectionListener(selectionListener); + } + + /** + * Inserts the given node to the given {@code parentNode}. + * <p/> + * The node will be added to his parent, then creation listeners will be + * fired. + * + * @param parentNode the parent node where to insert the new node * + * @param newNode the node to insert + */ + public void insertNode(N parentNode, N newNode) { + parentNode.add(newNode); + if (!model.isTreeTable()) { + model.notifyNodeInserted(newNode); + } else { + model.nodeStructureChanged(parentNode); + } + } + + /** + * Removes the given {@code node} from the registred tree model and returns + * his parent. + * + * @param node the node to remove + * @return the parent node of the removed node. + */ + public N removeNode(N node) { + N parentNode = node.getParent(); + model.removeNodeFromParent(node); + return parentNode; + } + + /** + * Moves the given {@code node} to the new {@code position}. + * + * @param parentNode the parent node + * @param node the node to move + * @param position the new position of the node + */ + public void moveNode(N parentNode, N node, int position) { + parentNode.remove(node); + parentNode.insert(node, position); + model.nodeStructureChanged(parentNode); + } + + /** + * Refreshs the given {@code node}. + * <p/> + * If flag {@code deep} is set to {@code true}, then it will refresh + * recursively children nodes. + * <p/> + * <b>Note:</b>As a side-effect, evvery node involved will become + * {@code dirty}. + * + * @param node the node to refresh + * @param deep un flag pour activer la repainte de la descendance du + * noeud + * @see jaxx.runtime.swing.nav.JaxxNode#isDirty() + */ + @SuppressWarnings({"unchecked"}) + public void refreshNode(N node, boolean deep) { + if (log.isDebugEnabled()) { + log.debug("Will refresh (deep ? " + deep + ") node " + node); + } + model.nodeChanged(node); + if (deep) { + // repaint childs nodes + Enumeration<N> e = node.children(); + while (e.hasMoreElements()) { + N child = e.nextElement(); + refreshNode(child, true); + } + } + } + + /** + * To load all nodes of a model. + * + * @param model the tree model owner of nodes + * @param node the root node to load + * @param dataProvider the data provider used to populate nodes + */ + @SuppressWarnings({"unchecked"}) + public void loadAllNodes(JaxxDelegateTreeModel<N> model, + N node, + DataProvider dataProvider) { + if (!checkModel()) { + return; + } + if (!node.isLoaded()) { + node.populateChilds(model, dataProvider); + Enumeration<? extends JaxxNode<?>> enumeration = node.children(); + while (enumeration.hasMoreElements()) { + N jaxxNode = (N) enumeration.nextElement(); + loadAllNodes(model, jaxxNode, dataProvider); + } + } + } + + /** + * Selects the parent of the currently selected node. + * <p/> + * <b>Note:</> If selection is empty, then throws a NPE. + * + * @throws NullPointerException if selection is empty + */ + public void selectParentNode() throws NullPointerException { + + N node = getSelectedNode(); + + if (node == null) { + // pas de noeud selectionne + throw new NullPointerException("no selected node in context"); + } + node = node.getParent(); + + selectNode(node); + } + + /** + * Selects the given {@code node} in the registred tree. + * + * @param node the node to select + */ + public void selectNode(N node) { + if (!checkModel()) { + + // no model + return; + } + if (log.isDebugEnabled()) { + log.debug("try to select node [" + node + "]"); + } + TreePath path = new TreePath(model.getPathToRoot(node)); + + setSelectionPath(path); + scrollPathToVisible(path); + } + + /** + * Selects the node described by his given {@code path} of ids. + * + * @param path the absolute path of ids from root node to node to select. + */ + public void selectNode(String... path) { + if (!checkModel()) { + + // no model + return; + } + if (log.isDebugEnabled()) { + log.debug("try to select node from ids " + Arrays.toString(path)); + } + N root = model.getRoot(); + N node = findNode(root, path); + if (log.isDebugEnabled()) { + log.debug("selected node [" + node + "]"); + } + if (node != null) { + selectNode(node); + } + } + + /** + * Finds a node from the given root {@code node}, applying the path given + * by {@code ids}. + * + * @param node the starting node + * @param ids the path of ids to apply on the node. + * @return the find node or {@code null} if no node matchs. + */ + public N findNode(N node, String... ids) { + if (!checkModel()) { + + // no model + return null; + } + N result = null; + for (String id : ids) { + + result = node.findNodeById(id, model, getDataProvider()); + + if (result == null) { + + // un des noeud n'a pas ete trouve, on sort + break; + } + node = result; + } + return result; + } + + /** + * Register a new root node. + * <p/> + * If internal {@link #model} does not exists, creates a new one from his given root {@code node}, + * otherwise just set the new root on the existing model. + * <p/> + * <p/> + * <b>Note:</b> As a side-effect, the model will be keep in field {@link #model} + * and the {@link #treeModelListener} will be registred on this model. + * + * @param node the root node of the new model + * @return the new model + * @deprecated use registerTreeModel + */ + @Deprecated + protected JaxxDelegateTreeModel createModel(N node) { + return createTreeModel(node); + } + + /** + * Register a new root node for tree. + * + * If internal {@link #model} does not exists, creates a new one from his given root {@code node}, + * otherwise just set the new root on the existing model. + * <p/> + * + * <b>Note:</b> As a side-effect, the model will be keep in field {@link #model} + * and the {@link #treeModelListener} will be registred on this model. + * + * @param node the root node of the new model + * @return the new model + */ + protected JaxxDelegateTreeModel<N> createTreeModel(N node) { + DefaultTreeModel delegate = new DefaultTreeModel(node); + if (model == null) { + model = new JaxxDelegateTreeModel<N>(delegate); + model.addTreeModelListener(treeModelListener); + } else { + model.setRoot(node); + } + + // notify structure has changed + model.nodeStructureChanged(getRootNode()); + return model; + } + + /** + * Register a new root node for tree table. + * + * If internal {@link #model} does not exists, creates a new one from his given root {@code node}, + * otherwise just set the new root on the existing model. + * <p/> + * + * <b>Note:</b> As a side-effect, the model will be keep in field {@link #model} + * and the {@link #treeModelListener} will be registred on this model. + * + * @param delegate model for tree table + * @param node the root node of the new model + * @return the tree model model + */ + protected JaxxDelegateTreeModel<N> createTreeTableModel(JaxxTreeTableModel.MyDefaultTreeTableModel delegate, N node) { + JaxxTreeTableModel tableModel = new JaxxTreeTableModel(delegate); + if (model == null) { + model = new JaxxDelegateTreeModel<N>(tableModel); + model.addTreeModelListener(treeModelListener); + } + model.setRoot(node); + + // notify structure has changed + model.nodeStructureChanged(getRootNode()); + return model; + } + + /** + * Checks if internal model was created. + * + * @return {@code true} if model was created + * (should be done via {@link #createModel(JaxxNode)} method), + * {@code false} otherwise. + */ + protected boolean checkModel() { + if (model == null) { + + // no model set, + log.warn("No model set in " + this); + return false; + } + // model is set + return true; + } + + /** + * Populates nodes. + * <p/> + * If {@code node} is not {@code null}, then populate it. + * <p/> + * If {@code children} is not {@code null}, then populate them, moreover + * if {@code recurse} is set to {@code true} then do a recurse refresh on + * children. + * + * @param node the parent node to populate (optional) + * @param children the child nodes to populate (optional) + * @param recurse flag sets to {@code true} if should do recurse refresh on + * given {@code children} nodes. + */ + @SuppressWarnings({"unchecked"}) + protected void populateNode(N node, + Object[] children, + boolean recurse) { + DataProvider dataProvider = getDataProvider(); + if (node != null) { + if (log.isDebugEnabled()) { + log.debug("Will populate node : " + node); + } + node.populateNode(model, dataProvider, false); + } + if (children != null) { + for (Object o : children) { + N child = (N) o; + if (log.isDebugEnabled()) { + log.debug("Will populate child node : " + child); + } + child.populateNode(model, dataProvider, recurse); + } + } + } + + /** + * Convinient method to objet the casted node of a {@link TreePath}. + * + * @param path the path contaning the node. + * @return the casted node from the path. + */ + @SuppressWarnings({"unchecked"}) + protected N getNode(TreePath path) { + N result = (N) path.getLastPathComponent(); + return result; + } + + //-------------------------------------------------------------------------- + //-- Methode to switch between tree and tree table + //-------------------------------------------------------------------------- + + public void scrollPathToVisible(TreePath path) { + JTree tree = getTree(); + if (tree != null) { + tree.scrollPathToVisible(path); + return; + } + getTreeTable().scrollPathToVisible(path); + } + + public void setSelectionPath(TreePath path) { + JTree tree = getTree(); + if (tree != null) { + tree.setSelectionPath(path); + return; + } + getTreeTable().getTreeSelectionModel().setSelectionPath(path); + } + + public TreeSelectionModel getSelectionModel() { + JTree tree = getTree(); + if (tree != null) { + return tree.getSelectionModel(); + } + return getTreeTable().getTreeSelectionModel(); + } + + public boolean isExpanded(TreePath pathToExpand) { + JTree tree = getTree(); + if (tree != null) { + return tree.isExpanded(pathToExpand); + } + return getTreeTable().isExpanded(pathToExpand); + } + + public void expandPath(TreePath pathToExpand) { + JTree tree = getTree(); + if (tree != null) { + tree.expandPath(pathToExpand); + return; + } + getTreeTable().expandPath(pathToExpand); + } +} Property changes on: trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/tree/JaxxTreeHelper.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision HeadURL Added: trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/tree/JaxxTreeNode.java =================================================================== --- trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/tree/JaxxTreeNode.java (rev 0) +++ trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/tree/JaxxTreeNode.java 2010-06-29 16:09:09 UTC (rev 2009) @@ -0,0 +1,480 @@ +/* + * #%L + * JAXX :: Runtime + * + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2008 - 2010 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package jaxx.runtime.swing.nav.tree; + +import jaxx.runtime.swing.nav.DataProvider; +import jaxx.runtime.swing.nav.JaxxDelegateTreeModel; +import jaxx.runtime.swing.nav.JaxxNode; +import jaxx.runtime.swing.nav.JaxxNodeChildLoador; +import org.apache.commons.logging.LogFactory; +import org.apache.commons.logging.Log; + +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.TreeNode; +import java.util.Enumeration; + +/** + * Definition of a node with a optional {@link #childLoador} to build childs of + * node. + * <p/> + * A node is identified by an {@link #id} of an associated data of type + * {@link #internalClass}. + * <p/> + * <b>Note:</b> + * <p><i> While using a {@link #childLoador}, we can not know before node + * was loaded the exact count of his childs. As a matter of facts, real leaf + * nodes appears at the beginning in ui as a not leaf (there is a root handler). + * When node was loaded, a leaf node will be then displayed as required. + * </i></p> + * <p/> + * <h2>Why JaxxNode is generic ?</h2> + * In a project, you should implements your own Node extending with one like this : + * <pre> + * class MyNode extends JaxxNode<MyNode> { ... } + * </pre> + * While in this class, you overrides every method with a node return type, + * co-variance you'll be able to use this code : + * <pre> + * MyNode parentNode = new MyNode(); + * MyNode node = parentNode.getFirstNode(); + * </pre> + * So for final application this generic type avoid any cast for your own node + * type, this is quite convinient. + * <p/> + * Even if in your project, you wants to have a heriarchy of nodes, this will + * still works (if you use a genercic type on your abstract nodes). + * <h2>Internal states</h2> + * <ul> + * <li><b>internalClass</b> : the type of data associated with the node</li> + * <li><b>context</b> : an optinal context to distinguish different types of + * node with same {@code internalclass}</li> + * <li><b>id</b> : id of the data associated with the node</li> + * <li><b>dirty</b> : flag sets to {@code true} when node render MUST be recomputed</li> + * <li><b>loaded</b> : flag sets to {@code true} when node was loaded</li> + * <li><b>childLoador</b> : optional loador of childs</li> + * </ul> + * <h2>Static nodes</h2> + * Some nodes do not need auto-loading, we call them {@code static nodes}. + * The method {@link #isStaticNode()} gives this state. + * <p/> + * <b>Note:</b> A static node has no {@link #childLoador}. + * <h2>Node loading</h2> + * Initialy node has no data child nodes, ({@link #isLoaded()} equals + * {@code false}). + * when model requires node's childs, it can load them via method + * {@link #populateNode(javax.swing.tree.DefaultTreeModel , jaxx.runtime.swing.nav.DataProvider , boolean)} + * and {@link #populateChilds(javax.swing.tree.DefaultTreeModel , jaxx.runtime.swing.nav.DataProvider)} methods. + * <h2>Node rendering</h2 + * the {@link AbstractJaxxTreeCellRenderer} looks the {@link #dirty} state to + * know when render should be (re-)compute and set back the state to {@code false}. + * <p/> + * Each time, a node is modified, the {@link #dirty} should be set to {@code true}. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.1 + */ +public class JaxxTreeNode<N extends JaxxTreeNode<N>> extends DefaultMutableTreeNode implements JaxxNode<N> { + + /** Logger */ + static private final Log log = LogFactory.getLog(JaxxNode.class); + + private static final long serialVersionUID = 1L; + + /** Type of data associated with the node */ + protected final Class<?> internalClass; + + /** + * Optinal context to distinguish different nodes with same + * {@link #internalClass}. + */ + protected final String context; + + /** Id of the data associated with the node. */ + protected final String id; + + /** Flag to know when renderer should (re-)compute render of the node. */ + protected boolean dirty = true; + + /** Flag to know when the none static node was loaded. */ + protected boolean loaded; + + /** Optional child loador to lazy create childs of the node. */ + protected final JaxxNodeChildLoador<?, ?, N> childLoador; + + protected JaxxTreeNode(String id) { + this(String.class, id, null, null); + } + + public JaxxTreeNode(Class<?> internalClass, + String id, + String context, + JaxxNodeChildLoador<?, ?, N> childLoador) { + this.internalClass = internalClass; + this.id = id; + this.context = context; + this.childLoador = childLoador; + if (isStaticNode()) { + + // A static node is always full loaded + loaded = true; + } + if (log.isDebugEnabled()) { + log.debug("new node : " + this); + } + } + + public String getId() { + return id; + } + + public String getContext() { + return context; + } + + public Class<?> getInternalClass() { + return internalClass; + } + + public boolean isLoaded() { + return loaded; + } + + public boolean isDirty() { + return dirty; + } + + /** + * Convinient method to known if the node is a {@code String} typed. + * + * @return {@code true} if the type of node if + */ + public boolean isStringNode() { + return String.class.equals(internalClass); + } + + /** + * To know if the node is static. + * <p/> + * A {@code static} node has no {@link #childLoador}. + * + * @return {@code true} when the node is static : says, the node has + * no {@link #childLoador}. + */ + public boolean isStaticNode() { + return childLoador == null; + } + + /** + * Gets the first node form this one to the root which has a none + * {@code String} type. + * + * @return the first concrete node type + */ + @SuppressWarnings({"unchecked"}) + public N getContainerNode() { + if (isRoot()) { + // si on arrive sur le root, quelque chose ne va pas, + // on bloque par null, a defaut de declancher une exception + return null; + } + + if (isStringNode()) { + // on est sur un noeud de type String, donc on regarde sur le parent + return getParent().getContainerNode(); + } + + // cas final : sur un noeud de donnee + classe interne de donnee + return (N) this; + } + + /** + * Given an {@code id}, obtain the child with matching id. + * <p/> + * If node is NOT {@code loaded}, then first loads it (method + * {@link #populateChilds(jaxx.runtime.swing.nav.JaxxDelegateTreeModel , jaxx.runtime.swing.nav.DataProvider)}) then do search + * on direct childs of the node. + * + * @param id the id of the researched node + * @param model model owner of nodes + * @param provider data provider + * @return the found node or {@code null} if not found + */ + @SuppressWarnings({"unchecked"}) + public N findNodeById(String id, + JaxxDelegateTreeModel<N> model, + DataProvider provider) { + if (id == null) { + + // id null ? donc rien a faire + return null; + } + if (id.equals(getId())) { + + // on a trouve le bon noeud + return (N) this; + } + + if (!isLoaded()) { + + // il faut charger les fils du noeud pour effectuer la recherche + populateChilds(model, provider); + } + + if (isLeaf()) { + + // au final le noeud est une feuille, donc ne convient pas + return null; + } + + // on recherche dans les fils + Enumeration<N> enumeration = children(); + while (enumeration.hasMoreElements()) { + N node = enumeration.nextElement(); + N nodeById = node.findNodeById(id, model, provider); + if (nodeById != null) { + return nodeById; + } + } + + // aucun des noeud fils ne convient + return null; + } + + /** + * Changes the {@link #dirty} state. + * <p/> + * As a side effect, when a renderer will use this node, it will force to + * reload the render from the {@link DataProvider}. + * + * @param dirty the new dirty value + */ + public void setDirty(boolean dirty) { + this.dirty = dirty; + } + + @Override + public boolean isLeaf() { + // there is two behaviours for the test : + // 1 - when the node is static, then can directly use his number of child + // to determine if node is a leaf (no child) + // 2 - when the node is dynamic, then ALWAYS says the node is NOT a leaf until + // it was loaded, otherwise the WillExpand listener will not load the childs... + // Once the node is loaded, use back the normal behaviour (count number of childs) + return isStaticNode() ? super.isLeaf() : isLoaded() && getChildCount() == 0; + } + + @Override + public Object getUserObject() { + return id; + } + + @Override + public String toString() { + return System.identityHashCode(this) + "-" + id; + } + + //-------------------------------------------------------------------------- + //-- Populate methods + //-------------------------------------------------------------------------- + + /** + * To populate the node. A side-effect of this method is to set {@code dirty} + * the node (renderer will recompute the render of the node). + * <p/> + * If {@code populateChilds} is set to {@code true}, then also populate + * childs of the node using the given {@code dataProvider}. + * + * @param model le modèles content le noeud + * @param provider le provider de données + * @param populateChilds un drapeau pour charger aussi les fils du noeud courant + */ + public void populateNode(JaxxDelegateTreeModel<N> model, + DataProvider provider, + boolean populateChilds) { + + // on indique que le noeud n'est plus propre + setDirty(true); + + if (populateChilds) { + + // chargement des fils + populateChilds(model, provider); + } + } + + /** + * To populate childs of the node (only when a none static node). + * A side-effect of this method is to set {@code loaded} of the node. + * <p/> + * For a static node, do nothing. + * + * @param model model owner of the node + * @param provider data provider + */ + @SuppressWarnings({"unchecked"}) + public void populateChilds(JaxxDelegateTreeModel<N> model, DataProvider provider) { + if (isStaticNode()) { + + log.debug("is static node " + this); + + // noeud static, rien a faire + return; + } + + // chargement des noeuds fils du noeud courant + try { + if (log.isDebugEnabled()) { + log.debug("Will load childs for " + this); + } + childLoador.loadChilds(model, (N) this, provider); + } catch (Exception e) { + throw new RuntimeException(e.getMessage(), e); + } finally { + + // au final, on passe le noeud a l'état chargé + loaded = true; + } + } + + //-------------------------------------------------------------------------- + //-- Overrides to use generic type as return + //-------------------------------------------------------------------------- + + @SuppressWarnings({"unchecked"}) + @Override + public N getParent() { + return (N) super.getParent(); + } + + @SuppressWarnings({"unchecked"}) + @Override + public N getRoot() { + return (N) super.getRoot(); + } + + @SuppressWarnings({"unchecked"}) + @Override + public N getFirstChild() { + return (N) super.getFirstChild(); + } + + @SuppressWarnings({"unchecked"}) + @Override + public N getLastChild() { + return (N) super.getLastChild(); + } + + @SuppressWarnings({"unchecked"}) + @Override + public N getChildAfter(TreeNode aChild) { + return (N) super.getChildAfter(aChild); + } + + @SuppressWarnings({"unchecked"}) + @Override + public N getChildBefore(TreeNode aChild) { + return (N) super.getChildBefore(aChild); + } + + @SuppressWarnings({"unchecked"}) + @Override + public N getNextSibling() { + return (N) super.getNextSibling(); + } + + @SuppressWarnings({"unchecked"}) + @Override + public N getPreviousSibling() { + return (N) super.getPreviousSibling(); + } + + @SuppressWarnings({"unchecked"}) + @Override + public N getFirstLeaf() { + return (N) super.getFirstLeaf(); + } + + @SuppressWarnings({"unchecked"}) + @Override + public N getLastLeaf() { + return (N) super.getLastLeaf(); + } + + @SuppressWarnings({"unchecked"}) + @Override + public N getNextLeaf() { + return (N) super.getNextLeaf(); + } + + @SuppressWarnings({"unchecked"}) + @Override + public N getPreviousLeaf() { + return (N) super.getPreviousLeaf(); + } + + @SuppressWarnings({"unchecked"}) + @Override + public N getNextNode() { + return (N) super.getNextNode(); + } + + @SuppressWarnings({"unchecked"}) + @Override + public N getPreviousNode() { + return (N) super.getPreviousNode(); + } + + @SuppressWarnings({"unchecked"}) + public N getSharedAncestor(N aNode) { + return (N) super.getSharedAncestor((DefaultMutableTreeNode)aNode); + } + + @SuppressWarnings({"unchecked"}) + @Override + public N getChildAt(int index) { + return (N) super.getChildAt(index); + } + + @SuppressWarnings({"unchecked"}) + @Override + public Enumeration<N> children() { + return (Enumeration<N>)super.children(); + } + + @Override + public void add(N node) { + super.add(node); + } + + @Override + public void remove(N node) { + super.remove(node); + } + + @Override + public void insert(N node, int position) { + super.insert(node, position); + } +} Added: trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/tree/JaxxTreeNodeChildLoador.java =================================================================== --- trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/tree/JaxxTreeNodeChildLoador.java (rev 0) +++ trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/tree/JaxxTreeNodeChildLoador.java 2010-06-29 16:09:09 UTC (rev 2009) @@ -0,0 +1,34 @@ +/* + * #%L + * JAXX :: Runtime + * + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2008 - 2010 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package jaxx.runtime.swing.nav.tree; + +import jaxx.runtime.swing.nav.JaxxNodeChildLoador; + +public abstract class JaxxTreeNodeChildLoador<T, O, N extends JaxxTreeNode<N>> extends JaxxNodeChildLoador<T, O, N> { + + protected JaxxTreeNodeChildLoador(Class<O> beanType) { + super(beanType); + } +} Added: trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/treetable/JaxxTreeTableModel.java =================================================================== --- trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/treetable/JaxxTreeTableModel.java (rev 0) +++ trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/treetable/JaxxTreeTableModel.java 2010-06-29 16:09:09 UTC (rev 2009) @@ -0,0 +1,217 @@ +/* + * #%L + * JAXX :: Runtime + * + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2008 - 2010 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package jaxx.runtime.swing.nav.treetable; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jdesktop.swingx.tree.TreeModelSupport; +import org.jdesktop.swingx.treetable.DefaultTreeTableModel; +import org.jdesktop.swingx.treetable.TreeTableModel; +import org.jdesktop.swingx.treetable.TreeTableNode; + +import javax.swing.event.TreeModelListener; +import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; + +/** + * Model of the tree table used for a jaxx tree table api. + * + * @author sletellier + * @since 2.2 + */ +public class JaxxTreeTableModel implements TreeTableModel { + + /** + * Logger + */ + static private final Log log = + LogFactory.getLog(JaxxTreeTableModel.class); + + + /** + * Hack to acces to the modelSupport + * + * @author sletellier + * @since 2.2 + */ + public static abstract class MyDefaultTreeTableModel extends DefaultTreeTableModel { + + public TreeModelSupport getModelSupport() { + return modelSupport; + } + + public abstract String[] getColumnsNames(); + + } + + /** + * the delegate model + */ + protected MyDefaultTreeTableModel delegate; + + public JaxxTreeTableModel(MyDefaultTreeTableModel delegate) { + this.delegate = delegate; + } + + @SuppressWarnings({"SuspiciousSystemArraycopy"}) + public TreeTableNode[] getPathToRoot(TreeTableNode aNode) { + if (aNode == null) { + return null; + } + TreeNode[] treeNodes = getDelegate().getPathToRoot(aNode); + JaxxTreeTableNode[] result = new JaxxTreeTableNode[treeNodes.length]; + System.arraycopy(treeNodes, 0, result, 0, treeNodes.length); + return result; + } + + public void nodeStructureChanged(JaxxTreeTableNode<?> node) { + if (node != null) { + JaxxTreeTableNode<?> parentNode = node.getParent(); + if (parentNode == null || parentNode.isRoot()) { + getModelSupport().fireNewRoot(); + } else { + TreeNode[] treeNodes = getPathToRoot(parentNode); + if (treeNodes != null) { + getModelSupport().fireTreeStructureChanged(new TreePath(treeNodes)); + } + // FIXME : it's append.... +// else { +// log.error("[Node structure changed] Path to root is null !"); +// } + } + } else { + log.error("Node is null !"); + } + } + + public void nodeChanged(JaxxTreeTableNode<?> node) { + if (node != null) { + JaxxTreeTableNode<?> parent = node.getParent(); + TreeNode[] treeNodes = getPathToRoot(parent); + if (treeNodes != null) { + getModelSupport().fireChildChanged( + new TreePath(treeNodes), parent.getIndex(node), node); + // FIXME : it's append.... +// else { +// log.error("[Node changed] Path to root is null !"); +// } + } + } else { + log.error("Node is null !"); + } + } + + public MyDefaultTreeTableModel getDelegate() { + return delegate; + } + + public TreeModelSupport getModelSupport() { + return delegate.getModelSupport(); + } + + public String[] getColomnsNames() { + return delegate.getColumnsNames(); + } + + public void setRoot(TreeTableNode root) { + delegate.setRoot(root); + } + + @Override + public TreeTableNode getRoot() { + return delegate.getRoot(); + } + + @Override + public Object getChild(Object parent, int index) { + return delegate.getChild(parent, index); + } + + @Override + public int getChildCount(Object parent) { + return delegate.getChildCount(parent); + } + + @Override + public boolean isLeaf(Object node) { + return delegate.isLeaf(node); + } + + @Override + public void valueForPathChanged(TreePath path, Object newValue) { + delegate.valueForPathChanged(path, newValue); + } + + @Override + public int getIndexOfChild(Object parent, Object child) { + return delegate.getIndexOfChild(parent, child); + } + + @Override + public void addTreeModelListener(TreeModelListener l) { + delegate.addTreeModelListener(l); + } + + @Override + public void removeTreeModelListener(TreeModelListener l) { + delegate.removeTreeModelListener(l); + } + + @Override + public Class<?> getColumnClass(int i) { + return getDelegate().getColumnClass(i); + } + + @Override + public int getColumnCount() { + return getColomnsNames().length; + } + + @Override + public String getColumnName(int column) { + return getColomnsNames()[column]; + } + + @Override + public int getHierarchicalColumn() { + return getDelegate().getHierarchicalColumn(); + } + + @Override + public Object getValueAt(Object o, int i) { + return getDelegate().getValueAt(o, i); + } + + @Override + public boolean isCellEditable(Object o, int i) { + return getDelegate().isCellEditable(o, i); + } + + @Override + public void setValueAt(Object o, Object o1, int i) { + getDelegate().setValueAt(o, o1, i); + } + +} Added: trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/treetable/JaxxTreeTableNode.java =================================================================== --- trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/treetable/JaxxTreeTableNode.java (rev 0) +++ trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/treetable/JaxxTreeTableNode.java 2010-06-29 16:09:09 UTC (rev 2009) @@ -0,0 +1,418 @@ +/* + * #%L + * JAXX :: Runtime + * + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2008 - 2010 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package jaxx.runtime.swing.nav.treetable; + +import jaxx.runtime.swing.nav.DataProvider; +import jaxx.runtime.swing.nav.JaxxDelegateTreeModel; +import jaxx.runtime.swing.nav.JaxxNodeChildLoador; +import jaxx.runtime.swing.nav.JaxxNode; +import org.apache.commons.logging.LogFactory; +import org.apache.commons.logging.Log; +import org.jdesktop.swingx.treetable.DefaultMutableTreeTableNode; +import java.util.Enumeration; + +/** + * Definition of a node with a optional {@link #childLoador} to build childs of + * node. + * <p/> + * A node is identified by an {@link #id} of an associated data of type + * {@link #internalClass}. + * <p/> + * <b>Note:</b> + * <p><i> While using a {@link #childLoador}, we can not know before node + * was loaded the exact count of his childs. As a matter of facts, real leaf + * nodes appears at the beginning in ui as a not leaf (there is a root handler). + * When node was loaded, a leaf node will be then displayed as required. + * </i></p> + * <p/> + * <h2>Why JaxxNode is generic ?</h2> + * In a project, you should implements your own Node extending with one like this : + * <pre> + * class MyNode extends JaxxNode<MyNode> { ... } + * </pre> + * While in this class, you overrides every method with a node return type, + * co-variance you'll be able to use this code : + * <pre> + * MyNode parentNode = new MyNode(); + * MyNode node = parentNode.getFirstNode(); + * </pre> + * So for final application this generic type avoid any cast for your own node + * type, this is quite convinient. + * <p/> + * Even if in your project, you wants to have a heriarchy of nodes, this will + * still works (if you use a genercic type on your abstract nodes). + * <h2>Internal states</h2> + * <ul> + * <li><b>internalClass</b> : the type of data associated with the node</li> + * <li><b>context</b> : an optinal context to distinguish different types of + * node with same {@code internalclass}</li> + * <li><b>id</b> : id of the data associated with the node</li> + * <li><b>dirty</b> : flag sets to {@code true} when node render MUST be recomputed</li> + * <li><b>loaded</b> : flag sets to {@code true} when node was loaded</li> + * <li><b>childLoador</b> : optional loador of childs</li> + * </ul> + * <h2>Static nodes</h2> + * Some nodes do not need auto-loading, we call them {@code static nodes}. + * The method {@link #isStaticNode()} gives this state. + * <p/> + * <b>Note:</b> A static node has no {@link #childLoador}. + * <h2>Node loading</h2> + * Initialy node has no data child nodes, ({@link #isLoaded()} equals + * {@code false}). + * when model requires node's childs, it can load them via method + * {@link #populateNode(javax.swing.tree.DefaultTreeModel , DataProvider, boolean)} + * and {@link #populateChilds(javax.swing.tree.DefaultTreeModel , DataProvider)} methods. + * <h2>Node rendering</h2 + * the {@link jaxx.runtime.swing.nav.tree.AbstractJaxxTreeCellRenderer} looks the {@link #dirty} state to + * know when render should be (re-)compute and set back the state to {@code false}. + * <p/> + * Each time, a node is modified, the {@link #dirty} should be set to {@code true}. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.1 + */ +public class JaxxTreeTableNode<N extends JaxxTreeTableNode<N>> extends DefaultMutableTreeTableNode implements JaxxNode<N> { + + /** Logger */ + static private final Log log = LogFactory.getLog(JaxxNode.class); + + private static final long serialVersionUID = 1L; + + /** Type of data associated with the node */ + protected final Class<?> internalClass; + + /** + * Optinal context to distinguish different nodes with same + * {@link #internalClass}. + */ + protected final String context; + + /** Id of the data associated with the node. */ + protected final String id; + + /** Flag to know when renderer should (re-)compute render of the node. */ + protected boolean dirty = true; + + /** Flag to know when the none static node was loaded. */ + protected boolean loaded; + + /** Optional child loador to lazy create childs of the node. */ + protected final JaxxNodeChildLoador childLoador; + + protected JaxxTreeTableNode(String id) { + this(String.class, id, null, null); + } + + public JaxxTreeTableNode(Class<?> internalClass, + String id, + String context, + JaxxNodeChildLoador childLoador) { + this.internalClass = internalClass; + this.id = id; + this.context = context; + this.childLoador = childLoador; + if (isStaticNode()) { + + // A static node is always full loaded + loaded = true; + } + if (log.isDebugEnabled()) { + log.debug("new node : " + this); + } + } + + public String getId() { + return id; + } + + public String getContext() { + return context; + } + + public Class<?> getInternalClass() { + return internalClass; + } + + public boolean isLoaded() { + return loaded; + } + + public boolean isDirty() { + return dirty; + } + + /** + * Convinient method to known if the node is a {@code String} typed. + * + * @return {@code true} if the type of node if + */ + public boolean isStringNode() { + return String.class.equals(internalClass); + } + + /** + * To know if the node is static. + * <p/> + * A {@code static} node has no {@link #childLoador}. + * + * @return {@code true} when the node is static : says, the node has + * no {@link #childLoador}. + */ + public boolean isStaticNode() { + return childLoador == null; + } + + /** + * Gets the first node form this one to the root which has a none + * {@code String} type. + * + * @return the first concrete node type + */ + @SuppressWarnings({"unchecked"}) + public N getContainerNode() { + if (isRoot()) { + // si on arrive sur le root, quelque chose ne va pas, + // on bloque par null, a defaut de declancher une exception + return null; + } + + if (isStringNode()) { + // on est sur un noeud de type String, donc on regarde sur le parent + return getParent().getContainerNode(); + } + + // cas final : sur un noeud de donnee + classe interne de donnee + return (N) this; + } + + public boolean isRoot() { + return getParent() == null; + } + + /** + * Given an {@code id}, obtain the child with matching id. + * <p/> + * If node is NOT {@code loaded}, then first loads it (method + * {@link #populateChilds(jaxx.runtime.swing.nav.JaxxDelegateTreeModel , DataProvider)}) then do search + * on direct childs of the node. + * + * @param id the id of the researched node + * @param model model owner of nodes + * @param provider data provider + * @return the found node or {@code null} if not found + */ + @SuppressWarnings({"unchecked"}) + public N findNodeById(String id, + JaxxDelegateTreeModel<N> model, + DataProvider provider) { + if (id == null) { + + // id null ? donc rien a faire + return null; + } + if (id.equals(getId())) { + + // on a trouve le bon noeud + return (N) this; + } + + if (!isLoaded()) { + + // il faut charger les fils du noeud pour effectuer la recherche + populateChilds(model, provider); + } + + if (isLeaf()) { + + // au final le noeud est une feuille, donc ne convient pas + return null; + } + + // on recherche dans les fils + Enumeration<N> enumeration = children(); + while (enumeration.hasMoreElements()) { + N node = enumeration.nextElement(); + N nodeById = node.findNodeById(id, model, provider); + if (nodeById != null) { + return nodeById; + } + } + + // aucun des noeud fils ne convient + return null; + } + + /** + * Changes the {@link #dirty} state. + * <p/> + * As a side effect, when a renderer will use this node, it will force to + * reload the render from the {@link DataProvider}. + * + * @param dirty the new dirty value + */ + public void setDirty(boolean dirty) { + this.dirty = dirty; + } + + @Override + public boolean isLeaf() { + // there is two behaviours for the test : + // 1 - when the node is static, then can directly use his number of child + // to determine if node is a leaf (no child) + // 2 - when the node is dynamic, then ALWAYS says the node is NOT a leaf until + // it was loaded, otherwise the WillExpand listener will not load the childs... + // Once the node is loaded, use back the normal behaviour (count number of childs) + return isStaticNode() ? super.isLeaf() : isLoaded() && getChildCount() == 0; + } + + @Override + public Object getUserObject() { + return id; + } + + @Override + public String toString() { + return System.identityHashCode(this) + "-" + id; + } + + //-------------------------------------------------------------------------- + //-- Populate methods + //-------------------------------------------------------------------------- + + /** + * To populate the node. A side-effect of this method is to set {@code dirty} + * the node (renderer will recompute the render of the node). + * <p/> + * If {@code populateChilds} is set to {@code true}, then also populate + * childs of the node using the given {@code dataProvider}. + * + * @param model le modèles content le noeud + * @param provider le provider de données + * @param populateChilds un drapeau pour charger aussi les fils du noeud courant + */ + public void populateNode(JaxxDelegateTreeModel<N> model, + DataProvider provider, + boolean populateChilds) { + + // on indique que le noeud n'est plus propre + setDirty(true); + + if (populateChilds) { + + // chargement des fils + populateChilds(model, provider); + } + } + + /** + * To populate childs of the node (only when a none static node). + * A side-effect of this method is to set {@code loaded} of the node. + * <p/> + * For a static node, do nothing. + * + * @param model model owner of the node + * @param provider data provider + */ + @SuppressWarnings({"unchecked"}) + public void populateChilds(JaxxDelegateTreeModel<N> model, DataProvider provider) { + if (isStaticNode()) { + + // noeud static, rien a faire + return; + } + + // chargement des noeuds fils du noeud courant + try { + if (log.isDebugEnabled()) { + log.debug("Will load childs for " + this); + } + childLoador.loadChilds(model, this, provider); + } catch (Exception e) { + throw new RuntimeException(e.getMessage(), e); + } finally { + + // au final, on passe le noeud a l'état chargé + loaded = true; + } + } + + //-------------------------------------------------------------------------- + //-- Overrides to use generic type as return + //-------------------------------------------------------------------------- + + @SuppressWarnings({"unchecked"}) + @Override + public N getParent() { + return (N) super.getParent(); + } + + @SuppressWarnings({"unchecked"}) + @Override + public N getChildAt(int index) { + return (N) super.getChildAt(index); + } + + @SuppressWarnings({"unchecked"}) + @Override + public Enumeration<N> children() { + return (Enumeration<N>)super.children(); + } + + @Override + public void add(N node) { + super.add(node); + } + + @SuppressWarnings({"unchecked"}) + public N[] getPathToRoot( + JaxxTreeTableNode aNode, int depth) { + JaxxTreeTableNode[] retNodes; + + /* Check for null, in case someone passed in a null node, or + they passed in an element that isn't rooted at root. */ + if (aNode == null) { + if (depth == 0) { + return null; + } else { + retNodes = new JaxxTreeTableNode[depth]; + } + } else { + depth++; + retNodes = getPathToRoot(aNode.getParent(), depth); + retNodes[retNodes.length - depth] = aNode; + } + return (N[]) retNodes; + } + + @Override + public void remove(N node) { + super.remove(node); + } + + @Override + public void insert(N node, int position) { + super.insert(node, position); + } +} Added: trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/treetable/JaxxTreeTableNodeChildLoador.java =================================================================== --- trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/treetable/JaxxTreeTableNodeChildLoador.java (rev 0) +++ trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/nav/treetable/JaxxTreeTableNodeChildLoador.java 2010-06-29 16:09:09 UTC (rev 2009) @@ -0,0 +1,34 @@ +/* + * #%L + * JAXX :: Runtime + * + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2008 - 2010 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package jaxx.runtime.swing.nav.treetable; + +import jaxx.runtime.swing.nav.JaxxNodeChildLoador; + +public abstract class JaxxTreeTableNodeChildLoador<T, O, N extends JaxxTreeTableNode<N>> extends JaxxNodeChildLoador<T, O, N> { + + protected JaxxTreeTableNodeChildLoador(Class<O> beanType) { + super(beanType); + } +} Modified: trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/navigation/treetable/NavigationTreeTableNode.java =================================================================== --- trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/navigation/treetable/NavigationTreeTableNode.java 2010-06-29 15:33:32 UTC (rev 2008) +++ trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/navigation/treetable/NavigationTreeTableNode.java 2010-06-29 16:09:09 UTC (rev 2009) @@ -46,7 +46,9 @@ * @author sletellier * @see NavigationTreeNode * @since 2.0.0 + * @deprecated since 2.1, prefer use the simplify api {@code jaxx.runtime.swing.tree}. */ +@Deprecated public class NavigationTreeTableNode extends DefaultMutableTreeTableNode implements NavigationNode<NavigationTreeTableNode> { private static final long serialVersionUID = -1L;