001    /*
002      Copyright (C) 2002-2003 Renaud Pawlak <renaud@aopsys.com>, 
003                              Laurent Martelli <laurent@aopsys.com>
004      
005      This program is free software; you can redistribute it and/or modify
006      it under the terms of the GNU Lesser General Public License as
007      published by the Free Software Foundation; either version 2 of the
008      License, or (at your option) any later version.
009    
010      This program is distributed in the hope that it will be useful, but
011      WITHOUT ANY WARRANTY; without even the implied warranty of
012      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013      Lesser General Public License for more details.
014    
015      You should have received a copy of the GNU Lesser General Public
016      License along with this program; if not, write to the Free Software
017      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
018      USA */
019    
020    package org.objectweb.jac.aspects.gui;
021    
022    import java.util.Arrays;
023    import java.util.Collection;
024    import java.util.Iterator;
025    import java.util.Vector;
026    import javax.swing.event.TreeSelectionEvent;
027    import javax.swing.tree.DefaultTreeModel;
028    import javax.swing.tree.TreePath;
029    import org.apache.log4j.Logger;
030    import org.objectweb.jac.core.NameRepository;
031    import org.objectweb.jac.core.rtti.ClassItem;
032    import org.objectweb.jac.core.rtti.ClassRepository;
033    import org.objectweb.jac.core.rtti.CollectionItem;
034    import org.objectweb.jac.core.rtti.FieldItem;
035    import org.objectweb.jac.core.rtti.MetaItem;
036    
037    /**
038     * A data model for trees.
039     */
040    
041    public class TreeModel extends DefaultTreeModel implements TreeView {
042        static Logger logger = Logger.getLogger("gui.treeview");
043        static Logger loggerPerf = Logger.getLogger("perf");
044    
045        Vector treeObjects = new Vector();
046        Vector treeNodes = new Vector();
047        boolean showRelations;
048        String rootObjects;
049    
050        /**
051         * Constructs a new tree model.
052         *
053         * @param rootNode the root node
054         * @param rootObjects ???
055         * @param showRelations tells if the tree should show the
056         * interobjects relations as nodes */
057        public TreeModel(RootNode rootNode, 
058                         String rootObjects, boolean showRelations) {
059            super(rootNode);
060            this.showRelations = showRelations;
061            this.rootObjects = rootObjects;
062            rootNode.setModel(this);
063            initTree(this,rootObjects,showRelations);
064        }
065    
066        public void setRootNode(AbstractNode root) {
067            setRoot(root);
068        }
069    
070        public void addNode(AbstractNode parent, AbstractNode child) {
071            logger.debug("addNode "+child+" to "+parent);
072            parent.add(child);
073        }
074    
075        public void setSelection(TreePath selection) {
076            Object[] listeners = listenerList.getListenerList();
077            TreeSelectionEvent e = null;
078            // Process the listeners last to first, notifying
079            // those that are interested in this event
080            for (int i = listeners.length-2; i>=0; i-=2) {
081                if (listeners[i]==TreeListener.class) {
082                    // Lazily create the event:
083                    ((TreeListener)listeners[i+1]).setSelection(selection);
084                }          
085            }
086        }
087    
088        public void addTreeListener(TreeListener listener) {
089            listenerList.add(TreeListener.class,listener);
090        }
091    
092        public void unregisterEvents() {
093            Iterator i = treeNodes.iterator();
094            while (i.hasNext()) {
095                AbstractNode node = (AbstractNode)i.next();
096                node.unregisterEvents();
097            }
098        }
099    
100        /**
101         * Add the children nodes for a node.
102         *
103         * @param rootNode the parent node for which to add children nodes.
104         * @param o the object represented by the rootNode
105         */
106        public static void addNodes(TreeView tree, ObjectNode rootNode,
107                                    Object o, boolean showRelations) {
108            logger.debug("addNodes("+rootNode+","+o+", showRelations="+showRelations+")...");
109            if (o==null)
110                return;
111            long start = System.currentTimeMillis();
112            // iterates on all the relations
113            ClassItem cli = ClassRepository.get().getClass(o);
114            FieldItem[] rels = (FieldItem[])cli.getAttribute(GuiAC.TREE_ATTRIBUTES_ORDER);
115            Collection related = null;
116            rootNode.setChildrenUptodate(true);
117            if (rels==null)
118                return;
119            for (int itRel=0; itRel<rels.length; itRel++) {
120                FieldItem rel = rels[itRel];
121                if (!GuiAC.isVisible(o,(MetaItem)rel)) 
122                    continue;
123                if (rel instanceof CollectionItem) {
124                    CollectionItem collection = (CollectionItem)rel;
125                    Collection cRel = collection.getActualCollectionThroughAccessor(o);
126                    related = cRel;
127                
128                    if (showRelations && 
129                        collection.getAttribute(GuiAC.HIDDEN_TREE_RELATION)==null) {
130                        // add a collection node
131                        RelationNode relationNode = 
132                            new RelationNode(tree,o,collection); 
133                        logger.debug("adding relation node for collection "+collection.getName());
134                        Utils.registerCollection(o,collection,relationNode);
135                        rootNode.add(relationNode);
136                        Iterator it = related.iterator();
137                        while (it.hasNext()) {
138                            Object newObject = it.next();
139                            ObjectNode newNode = new ObjectNode(tree,newObject,o,collection,showRelations); 
140                            newNode.setLeaf(isLeafNode(tree,newNode,o,showRelations));
141                            relationNode.add(newNode);
142                        }
143                    } else {
144                        logger.debug("adding nodes for collection "+collection.getName()+": "+
145                                  Arrays.asList(related.toArray()));
146                        Utils.registerCollection(o,collection,rootNode);
147                        // recursively add nodes
148                        Iterator it = related.iterator();
149                        while (it.hasNext()) {
150                            Object newObject = it.next();
151                            logger.debug("adding node for collection "+
152                                      collection.getName()+": "+newObject);
153                            ObjectNode newNode = new ObjectNode(tree,newObject,o,collection,showRelations); 
154                            newNode.setLeaf(isLeafNode(tree,newNode,newObject,showRelations));
155                            rootNode.addNode(newNode);                  
156                        }
157                    }
158                } else if (rel instanceof FieldItem) {
159                    if (showRelations &&
160                        rel.getAttribute(GuiAC.HIDDEN_TREE_RELATION)==null) 
161                    { 
162                        RelationNode relationNode = 
163                            new RelationNode(tree,o,rel);
164                        rootNode.add(relationNode);
165                        Object relatedObject = rel.getThroughAccessor(o);
166                        if (relatedObject!=null) {
167                            ObjectNode newNode = new ObjectNode(tree,relatedObject,o,rel,showRelations); 
168                            newNode.setLeaf(isLeafNode(tree,newNode,relatedObject,showRelations));
169                            relationNode.add(newNode);
170                        }
171                    } else {
172                        Object relatedObject = rel.getThroughAccessor(o);
173                        if (relatedObject!=null) {
174                            ObjectNode newNode = new ObjectNode(tree,relatedObject,o,rel,showRelations); 
175                            newNode.setLeaf(isLeafNode(tree,newNode,relatedObject,showRelations));
176                            rootNode.addNode(newNode);
177                        }
178                    }
179                }
180            }
181            loggerPerf.info("Added nodes in "+(System.currentTimeMillis()-start)+"ms");
182        }
183    
184    
185        /**
186         * Tells wether a node is a leaf or not, without computing all its
187         * children.
188         *
189         * @param tree The tree the node belongs to
190         * @param node the node
191         * @param o the object represented by the node
192         * @param showRelations wether to show a node for relations
193         */
194        public static boolean isLeafNode(TreeView tree, ObjectNode node,
195                                         Object o, boolean showRelations) {
196            if (o==null) 
197                return true;
198            // iterates on all the relations
199            ClassItem cli = ClassRepository.get().getClass(o);
200            FieldItem[] rels = (FieldItem[])cli.getAttribute(GuiAC.TREE_ATTRIBUTES_ORDER);
201            Collection related = null;
202            boolean isLeaf=true;
203            if (rels==null)
204                return true;
205            for (int itRel=0; itRel<rels.length; itRel++) {
206                FieldItem rel = rels[itRel];
207                if (!GuiAC.isVisible(o,(MetaItem)rel)) 
208                    continue;
209                if (rel instanceof CollectionItem) {
210                    CollectionItem collection = (CollectionItem)rel;
211                    Utils.registerCollection(o,collection,node);
212                    if (isLeaf && 
213                        !collection.getActualCollectionThroughAccessor(o).isEmpty()) {
214                        // !! we must not return here because the loop is also used
215                        // to register for updates (note that this should be done in
216                        // addNodes)
217                        isLeaf=false;
218                    }
219                             } else {
220                     Utils.registerField(o,rel,node);
221                                     if (isLeaf && rel.getThroughAccessor(o)!=null) {
222                                            isLeaf=false;
223                                     }
224                             }
225            }
226            return isLeaf;
227        }
228    
229        /**
230         * Initialize a tree view.
231         * @param tree the tree view to initialize
232         * @param rootObjects a regular expression designating root objects
233         * @param showRelations wether to build nodes for the relations themselves
234         */
235        public static void initTree(TreeView tree, String rootObjects, 
236                                    boolean showRelations) {
237            logger.debug("initTree "+rootObjects+"...");
238             
239            // the first accessible collection
240            AbstractNode rootNode = new RootNode();
241            tree.setRootNode(rootNode);
242            Iterator it = NameRepository.getObjects(rootObjects).iterator();
243            while (it.hasNext()) {
244                Object object = it.next();
245                ObjectNode objectNode = new ObjectNode(tree,object,null,null,showRelations); 
246                rootNode.add(objectNode);
247                objectNode.setLeaf(isLeafNode(tree,objectNode,object,showRelations));
248            }
249    
250            logger.debug("initTree "+rootObjects+" DONE");
251        }
252    
253    }