001/* A tree model for the Vergil library panel.
002
003 Copyright (c) 2000-2014 The Regents of the University of California.
004 All rights reserved.
005 Permission is hereby granted, without written agreement and without
006 license or royalty fees, to use, copy, modify, and distribute this
007 software and its documentation for any purpose, provided that the above
008 copyright notice and the following two paragraphs appear in all copies
009 of this software.
010
011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
015 SUCH DAMAGE.
016
017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
022 ENHANCEMENTS, OR MODIFICATIONS.
023
024 PT_COPYRIGHT_VERSION_2
025 COPYRIGHTENDKEY
026
027 */
028package ptolemy.vergil.tree;
029
030import java.util.Collections;
031import java.util.HashMap;
032import java.util.Iterator;
033import java.util.LinkedList;
034import java.util.List;
035import java.util.Map;
036
037import javax.swing.tree.TreePath;
038
039import ptolemy.kernel.CompositeEntity;
040import ptolemy.kernel.Entity;
041import ptolemy.kernel.util.Attribute;
042import ptolemy.kernel.util.NamedObj;
043import ptolemy.kernel.util.Workspace;
044import ptolemy.moml.EntityLibrary;
045import ptolemy.vergil.icon.EditorIcon;
046
047///////////////////////////////////////////////////////////////////
048//// VisibleTreeModel
049
050/**
051
052 A tree model for the Vergil library panel.  This is a tree model that
053 shows all entities and some ports, relations, and attributes.  The ports,
054 relations, and attributes that it shows are those that
055 contain an attribute of class EditorIcon, or that contain an
056 attribute named "_iconDescription" or "_smallIconDescription".
057 A composite entity that contains an attribute with name "_libraryMarker"
058 is treated as a sublibrary. A composite entity without such an attribute
059 is treated as an atomic entity.
060 This is designed for use with JTree, which renders the hierarchy.
061
062 @author Steve Neuendorffer and Edward A. Lee
063 @version $Id$
064 @since Ptolemy II 1.0
065 @Pt.ProposedRating Red (eal)
066 @Pt.AcceptedRating Red (johnr)
067 */
068public class VisibleTreeModel extends FullTreeModel {
069    /** Create a new tree model with the specified root.
070     *  @param root The root of the tree.
071     */
072    public VisibleTreeModel(CompositeEntity root) {
073        super(root);
074        _workspace = root.workspace();
075        _workspaceAttributeVersion = _workspace.getVersion();
076    }
077
078    ///////////////////////////////////////////////////////////////////
079    ////                         public methods                    ////
080
081    /** Return true if the object is a leaf node.  An object is a leaf
082     *  node if it has no children that are instances of one of the classes
083     *  specified by setFilter(), if a filter has been specified.
084     *  @param object The object.
085     *  @return True if the node has no children.
086     */
087    @Override
088    public boolean isLeaf(Object object) {
089        // NOTE: handle EntityLibrary specially to prevent evaluation
090        // of the library prematurely.
091        if (object instanceof EntityLibrary) {
092            return false;
093        }
094
095        // If the object is an instance of CompositeEntity, but does not
096        // contain an attribute named "_libraryMarker", then treat it as an
097        // atomic entity.
098        if (object instanceof CompositeEntity) {
099            Attribute marker = ((CompositeEntity) object)
100                    .getAttribute("_libraryMarker");
101
102            if (marker == null) {
103                return true;
104            }
105        }
106
107        // Defer to the parent for the rest.
108        return super.isLeaf(object);
109    }
110
111    /** Override the base class to do nothing. This seems risky, but
112     *  the problem is that without it, every time you expand a library
113     *  that has a image or PDF icon it, the entire library collapses,
114     *  which is extremely annoying. I guess this is OK under the
115     *  assumption that the library does not change during execution.
116     *  @param path The path of the node that has changed.
117     *  @param newValue The new value of the node.
118     */
119    @Override
120    public void valueForPathChanged(TreePath path, Object newValue) {
121        // Do nothing.
122    }
123
124    ///////////////////////////////////////////////////////////////////
125    ////                         protected methods                 ////
126
127    /** Return the list of attributes, or an empty list if there are none.
128     *  Override this method if you wish to show only a subset of the
129     *  attributes.
130     *  @param object The object.
131     *  @return A list of attributes.
132     */
133    @Override
134    protected List _attributes(Object object) {
135        if (!(object instanceof NamedObj)) {
136            return Collections.EMPTY_LIST;
137        }
138
139        // Use the cached list, if possible.
140        long version = _workspace.getVersion();
141
142        if (version == _workspaceAttributeVersion) {
143            Object result = _attributeListCache.get(object);
144
145            if (result != null) {
146                return (List) result;
147            }
148        }
149
150        // Cache is not valid.
151        List result = new LinkedList();
152        Iterator attributes = ((NamedObj) object).attributeList().iterator();
153
154        while (attributes.hasNext()) {
155            NamedObj attribute = (NamedObj) attributes.next();
156
157            if (_isVisible(attribute)) {
158                result.add(attribute);
159            }
160        }
161
162        _attributeListCache.put(object, result);
163        _workspaceAttributeVersion = _workspace.getVersion();
164        return result;
165    }
166
167    /** Return true if the object contains either an attribute of
168     *  class EditorIcon or an attribute of any class named
169     *  "_iconDescription" or "_smallIconDescription".  This
170     *  will result in the object being rendered in the library.
171     *  @param object The object.
172     *  @return True if the object is to be rendered in the library.
173     */
174    protected boolean _isVisible(NamedObj object) {
175        List iconList = object.attributeList(EditorIcon.class);
176
177        if (iconList.size() > 0
178                || object.getAttribute("_iconDescription") != null
179                || object.getAttribute("_smallIconDescription") != null) {
180            return true;
181        } else {
182            return false;
183        }
184    }
185
186    /** Return the list of ports, or an empty list if there are none.
187     *  Override this method if you wish to show only a subset of the
188     *  ports.
189     *  @param object The object.
190     *  @return A list of ports.
191     */
192    @Override
193    protected List _ports(Object object) {
194        if (!(object instanceof Entity)) {
195            return Collections.EMPTY_LIST;
196        }
197
198        // Use the cached list, if possible.
199        long version = _workspace.getVersion();
200
201        if (version == _workspacePortVersion) {
202            Object result = _portListCache.get(object);
203
204            if (result != null) {
205                return (List) result;
206            }
207        }
208
209        // Cache is not valid.
210        List result = new LinkedList();
211        Iterator ports = ((Entity) object).portList().iterator();
212
213        while (ports.hasNext()) {
214            NamedObj port = (NamedObj) ports.next();
215
216            if (_isVisible(port)) {
217                result.add(port);
218            }
219        }
220
221        _portListCache.put(object, result);
222        _workspacePortVersion = _workspace.getVersion();
223        return result;
224    }
225
226    /** Return the list of relations, or an empty list if there are none.
227     *  Override this method if you wish to show only a subset of the
228     *  relations.
229     *  @param object The object.
230     *  @return A list of relations.
231     */
232    @Override
233    protected List _relations(Object object) {
234        if (!(object instanceof CompositeEntity)) {
235            return Collections.EMPTY_LIST;
236        }
237
238        // Use the cached list, if possible.
239        long version = _workspace.getVersion();
240
241        if (version == _workspaceRelationVersion) {
242            Object result = _relationListCache.get(object);
243
244            if (result != null) {
245                return (List) result;
246            }
247        }
248
249        // Cache is not valid.
250        List result = new LinkedList();
251        Iterator relations = ((CompositeEntity) object).relationList()
252                .iterator();
253
254        while (relations.hasNext()) {
255            NamedObj relation = (NamedObj) relations.next();
256
257            if (_isVisible(relation)) {
258                result.add(relation);
259            }
260        }
261
262        _relationListCache.put(object, result);
263        _workspaceRelationVersion = _workspace.getVersion();
264        return result;
265    }
266
267    ///////////////////////////////////////////////////////////////////
268    ////                         private variables                 ////
269    // Workspace and version information.
270    private Workspace _workspace;
271
272    private long _workspaceAttributeVersion;
273
274    private long _workspacePortVersion;
275
276    private long _workspaceRelationVersion;
277
278    // Cache for visible attributes.
279    private Map _attributeListCache = new HashMap();
280
281    // Cache for visible ports.
282    private Map _portListCache = new HashMap();
283
284    // Cache for visible relations.
285    private Map _relationListCache = new HashMap();
286}