Clover coverage report - DrJava Test Coverage (drjava-20120422-r5456)
Coverage timestamp: Sun Apr 22 2012 03:13:25 CDT
file stats: LOC: 424   Methods: 48
NCLOC: 175   Classes: 2
 
 Source file Conditionals Statements Methods TOTAL
JListNavigator.java 76.7% 71.9% 54.2% 68.2%
coverage coverage
 1    /*BEGIN_COPYRIGHT_BLOCK
 2    *
 3    * Copyright (c) 2001-2010, JavaPLT group at Rice University (drjava@rice.edu)
 4    * All rights reserved.
 5    *
 6    * Redistribution and use in source and binary forms, with or without
 7    * modification, are permitted provided that the following conditions are met:
 8    * * Redistributions of source code must retain the above copyright
 9    * notice, this list of conditions and the following disclaimer.
 10    * * Redistributions in binary form must reproduce the above copyright
 11    * notice, this list of conditions and the following disclaimer in the
 12    * documentation and/or other materials provided with the distribution.
 13    * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
 14    * names of its contributors may be used to endorse or promote products
 15    * derived from this software without specific prior written permission.
 16    *
 17    * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 18    * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 19    * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 20    * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 21    * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 22    * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 23    * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 24    * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 25    * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 26    * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 27    * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 28    *
 29    * This software is Open Source Initiative approved Open Source Software.
 30    * Open Source Initative Approved is a trademark of the Open Source Initiative.
 31    *
 32    * This file is part of DrJava. Download the current version of this project
 33    * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
 34    *
 35    * END_COPYRIGHT_BLOCK*/
 36   
 37    package edu.rice.cs.util.docnavigation;
 38   
 39    import java.io.File;
 40    import java.awt.*;
 41    import javax.swing.*;
 42    import javax.swing.event.*;
 43    import java.util.*;
 44    import edu.rice.cs.util.swing.Utilities;
 45    //import edu.rice.cs.util.swing.RightClickMouseAdapter;
 46   
 47    /** This class is an extension of JList that adds data shadowing the model embedded in a JList.
 48    * Since all changes to the model (except for the selected item!) must go through this interface,
 49    * we can support access to methods from non-event threads as long as these methods do not modify
 50    * the model. However, all of the public methods that access and modify the model (the latter only running
 51    * in the event thread) must be atomic relative to each other, so synchronization is required in most
 52    * cases.
 53    *
 54    * TODO: generify this class and IDocumentNavigator with respect to its element type once JList is.
 55    */
 56   
 57    class JListNavigator<ItemT extends INavigatorItem> extends JList implements IDocumentNavigator<ItemT> {
 58   
 59    /** The list model (extending AbstractListModel) for this JList. */
 60    protected DefaultListModel _model;
 61   
 62    /** The current selection value. A cached copy of getSelectedValue(). */
 63    private volatile ItemT _current = null;
 64   
 65    // /** The index of _current */
 66    // private int _currentIndex = -1;
 67   
 68    /** The cell renderer for this JList */
 69    private volatile CustomListCellRenderer _renderer;
 70   
 71    /** the collection of INavigationListeners listening to this JListNavigator */
 72    private final ArrayList<INavigationListener<? super ItemT>> navListeners = new ArrayList<INavigationListener<? super ItemT>>();
 73   
 74    /** Standard constructor. */
 75  162 public JListNavigator() {
 76  162 super();
 77  162 init(new DefaultListModel());
 78    }
 79   
 80  162 private void init(DefaultListModel m) {
 81  162 _model = m;
 82  162 setModel(m);
 83  162 setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
 84  162 addListSelectionListener(new ListSelectionListener() {
 85    /** Called when the list value has changed. Should only run in the event thread.
 86    * @param e the event corresponding to the change
 87    */
 88  766 public void valueChanged(final ListSelectionEvent e) {
 89    // assert EventQueue.isDispatchThread();
 90    // Utilities.invokeLater( new Runnable() {
 91    // public void run() {
 92  766 if (!e.getValueIsAdjusting() && !_model.isEmpty()) {
 93  762 @SuppressWarnings("unchecked") final ItemT newItem = (ItemT) getSelectedValue();
 94    // final int newIndex = getSelectedIndex();
 95  762 if (_current != newItem) {
 96  489 final ItemT oldItem = _current;
 97  489 NodeData<ItemT> oldData = new NodeData<ItemT>() {
 98  0 public <Ret> Ret execute(NodeDataVisitor<? super ItemT, Ret> v, Object... p) {
 99  0 return v.itemCase(oldItem, p);
 100    }
 101    };
 102  489 NodeData<ItemT> newData = new NodeData<ItemT>() {
 103  389 public <Ret> Ret execute(NodeDataVisitor<? super ItemT, Ret> v, Object... p) {
 104  389 return v.itemCase(newItem, p);
 105    }
 106    };
 107  489 for(INavigationListener<? super ItemT> listener: navListeners) {
 108  284 if (oldItem != null) listener.lostSelection(oldData, isNextChangeModelInitiated());
 109  389 if (newItem != null) listener.gainedSelection(newData, isNextChangeModelInitiated());
 110    }
 111  489 setNextChangeModelInitiated(false);
 112  489 _current = newItem;
 113    // _currentIndex = newIndex;
 114    }
 115    }
 116    // }
 117    // });
 118    }
 119    });
 120   
 121  162 _renderer = new CustomListCellRenderer();
 122  162 _renderer.setOpaque(true);
 123  162 this.setCellRenderer(_renderer);
 124    }
 125   
 126    /** Adds the document doc to this navigator. Should only be executed in event thread.
 127    * @param doc the document to add
 128    */
 129  0 public void addDocument(ItemT doc) { synchronized(_model) { _model.addElement(doc); } }
 130   
 131    /** Adds the document to this navigator and ignores the specified path. Should only be
 132    * executed in event thread.
 133    * @param doc the document to add -- assumed to be of type T
 134    * @param path unused parameter in this class
 135    */
 136  68 public void addDocument(ItemT doc, String path) { addDocument(doc); }
 137   
 138    /** A typesafe version of {@code _model.get(i)}. This is a workaround for the
 139    * non-generic implementation of DefaultListModel, and should be removed once that
 140    * is fixed.
 141    */
 142  822 protected ItemT getFromModel(int i) {
 143  822 @SuppressWarnings("unchecked") ItemT result = (ItemT) _model.get(i);
 144  822 return result;
 145    }
 146   
 147    /** Gets the next document after doc in the series.
 148    * @param doc the document to reference from
 149    * @return the document after doc in the list; if doc is the last document, returns doc
 150    */
 151  31 public ItemT getNext(ItemT doc) {
 152  31 synchronized(_model) {
 153  31 int i = _model.indexOf(doc);
 154  31 if (i == -1)
 155  0 throw new IllegalArgumentException("No such document " + doc.toString() + " found in collection of open documents");
 156  7 if ( i + 1 == _model.size()) return doc;
 157   
 158  24 return getFromModel(i + 1);
 159    }
 160    }
 161   
 162    /** Gets the previous document in the series.
 163    * @param doc to reference from
 164    * @return the document which comes before doc in the list
 165    */
 166  10 public ItemT getPrevious(ItemT doc) {
 167  10 synchronized(_model) {
 168  10 int i = _model.indexOf(doc);
 169  10 if (i == -1)
 170  0 throw new IllegalArgumentException("No such document " + doc.toString() + " found in collection of open documents");
 171  2 if (i == 0) return doc;
 172  8 return getFromModel(i - 1);
 173    }
 174    }
 175   
 176    /** Gets the first document in the series.
 177    * @return the first document in the collection
 178    */
 179  7 public ItemT getFirst() { synchronized(_model) { return getFromModel(0); } }
 180   
 181    /** Gets the first document in the series.
 182    * @return the first document in the collection
 183    */
 184  3 public ItemT getLast() { synchronized(_model) { return getFromModel(_model.size() - 1); } }
 185   
 186    /** Returns the currently selected item, or null if none. */
 187  1 public ItemT getCurrent() { return _current; }
 188   
 189    /** Returns the model lock. */
 190  0 public Object getModelLock() { return _model; }
 191   
 192    /** Removes the document from the navigator. Should only be executed in event thread.
 193    * @param doc the document to remove
 194    */
 195  263 public ItemT removeDocument(ItemT doc) {
 196  263 synchronized(_model) {
 197    // System.err.println("removing from old list " + doc);
 198  263 int i = _model.indexOf(doc);
 199  263 if( i == -1 )
 200  0 throw new IllegalArgumentException("Document " + doc + " not found in Document Navigator");
 201  263 ItemT result = getFromModel(i);
 202  263 _model.remove(i);
 203  263 return result;
 204    }
 205    }
 206   
 207    /** Resets a given <code>INavigatorItem<code> in the tree. This may affect the placement of the item or its
 208    * display to reflect any changes made in the model. Should only be executed in event thread.
 209    * @param doc the docment to be refreshed
 210    * @throws IllegalArgumentException if this navigator contains no document that is equal to the passed document.
 211    */
 212  86 public void refreshDocument(ItemT doc, String path) {
 213  86 synchronized(_model) {
 214  86 removeDocument(doc);
 215  86 addDocument(doc);
 216    }
 217    }
 218   
 219    /** Sets the specified document as selected in the navigator. Only executes in event thread.
 220    * @param doc the document to select
 221    */
 222  398 public void selectDocument(ItemT doc) {
 223  398 /* */ assert Utilities.TEST_MODE || EventQueue.isDispatchThread();
 224  398 boolean found;
 225    // synchronized(_model) {
 226  8 if (_current == doc) return; // doc is already _current (the active doc)
 227  390 found = _model.contains(doc);
 228    // }
 229  390 if (found) setSelectedValue(doc, true);
 230    // _current = doc; // already done by ListSelectionEvent listener created in init()
 231    // }
 232    }
 233   
 234    /** Returns whether or not the navigator contains the document
 235    * @param doc the document to find
 236    * @return true if this list contains doc (using identity as equality measure), false if not.
 237    */
 238  5 public boolean contains(ItemT doc) {
 239  5 synchronized(_model) { return _model.contains(doc); }
 240    }
 241   
 242    /** @return an Enumeration of the documents in this list (ordering is consistent with getNext() and getPrev()).
 243    * This cast in this method required to work around the stupid partial generification of DefaultListModel in Java 1.5.
 244    * The class should be generic: DefaultListModel<T> { ... Enumeration<T> elements() {...} ... } instead of
 245    * DefaultListModel { ... Enumeration<?> elements() {...} ... }.
 246    */
 247  6 public ArrayList<ItemT> getDocuments() {
 248  6 synchronized(_model) {
 249    // Cast forced by lousy generic typing of DefaultListModel in Java 1.5
 250  6 @SuppressWarnings("unchecked") Enumeration<ItemT> items = (Enumeration<ItemT>) _model.elements();
 251  6 ArrayList<ItemT> result = new ArrayList<ItemT>(_model.size());
 252  9 while (items.hasMoreElements()) result.add(items.nextElement());
 253  6 return result;
 254    }
 255    }
 256   
 257    /** Returns all the <code>IDocuments</code> contained in the specified bin. Always empty.
 258    * @param binName name of bin
 259    * @return an <code>INavigatorItem<code> enumeration of this navigator's contents.
 260    */
 261  0 public ArrayList<ItemT> getDocumentsInBin(String binName) { return new ArrayList<ItemT>(0); }
 262   
 263    /** @return the number of documents in the navigator. */
 264  1 public int getDocumentCount() { return _model.size(); }
 265   
 266    /** @return whether or not the navigator is empty. */
 267  0 public boolean isEmpty() { return _model.isEmpty(); }
 268   
 269    /** Adds listener to the collection of listeners.
 270    * @param listener
 271    */
 272  161 public void addNavigationListener(INavigationListener<? super ItemT> listener) {
 273  161 synchronized(_model) { navListeners.add(listener); }
 274    }
 275   
 276    /** Unregisters the listener listener
 277    * @param listener
 278    */
 279  0 public void removeNavigationListener(INavigationListener<? super ItemT> listener) {
 280  0 synchronized(_model) { navListeners.remove(listener); }
 281    }
 282   
 283    /** @return the navigator listeners. */
 284  3 public Collection<INavigationListener<? super ItemT>> getNavigatorListeners() { return navListeners; }
 285   
 286    /** Clears the navigator and removes all documents. Should only be executed from event thread. */
 287  3 public void clear() { synchronized(_model) { _model.clear(); } }
 288   
 289    /** Executes the list case of the visitor.
 290    * @param algo the visitor to execute
 291    * @param input the input to run on the visitor
 292    */
 293  0 public <InType, ReturnType> ReturnType execute(IDocumentNavigatorAlgo<ItemT, InType, ReturnType> algo, InType input) {
 294  0 return algo.forList(this, input);
 295    }
 296   
 297    /** Returns a Container representation of this navigator */
 298  402 public Container asContainer() { return this; }
 299   
 300    /** Selects the document at the x,y coordinate of the navigator pane and sets it to be
 301    * the currently active document. Should only be called from event-handling thread.
 302    * @param x the x coordinate of the navigator pane
 303    * @param y the y coordinate of the navigator pane
 304    */
 305  0 public boolean selectDocumentAt(final int x, final int y) {
 306  0 synchronized(_model) {
 307  0 final int idx = locationToIndex(new java.awt.Point(x,y));
 308  0 java.awt.Rectangle rect = getCellBounds(idx, idx);
 309  0 if (rect.contains(x, y)) {
 310  0 selectDocument(getFromModel(idx));
 311  0 return true;
 312    }
 313  0 return false;
 314    }
 315    }
 316   
 317    /** Returns true if the item at the x,y coordinate of the navigator pane is currently selected.
 318    * Always false for JListSortNavigator
 319    * Only runs in event thread.
 320    * @param x the x coordinate of the navigator pane
 321    * @param y the y coordinate of the navigator pane
 322    * @return true if the item is currently selected
 323    */
 324  0 public boolean isSelectedAt(int x, int y) { return false;
 325    // synchronized(_model) {
 326    // final int idx = locationToIndex(new java.awt.Point(x,y));
 327    // if (idx == -1) return false;
 328    // return isSelectedIndex(idx);
 329    // }
 330    }
 331   
 332    /** @return the renderer for this object. */
 333  2 public Component getRenderer() { return _renderer; }
 334   
 335    /** @return the number of selected items. Always 1 for JListNavigator */
 336  0 public int getSelectionCount() { return 1; } // return getSelectedIndices().length; }
 337   
 338    /** @return true if at least one group of INavigatorItems is selected; always false for JListNavigator */
 339  0 public boolean isGroupSelected() { return false; }
 340   
 341    /** @return the number of groups selected. Always 0 for JListSortNavigator */
 342  0 public int getGroupSelectedCount() { return 0; }
 343   
 344    /** @return the folders currently selected. Always empty for JListSortNavigator */
 345  0 public java.util.List<File> getSelectedFolders() { return new ArrayList<File>(); }
 346   
 347    /** @return true if at least one document is selected; always true for JListNavigator */
 348  0 public boolean isDocumentSelected() { return true; }
 349   
 350    /** @return the number of documents selected. Same as getSelectionCount for JListSortNavigator. */
 351  0 public int getDocumentSelectedCount() { return getSelectionCount(); }
 352   
 353    /** @return the documents currently selected. Only runs in event thread. */
 354  1 @SuppressWarnings("unchecked") public java.util.List<ItemT> getSelectedDocuments() {
 355    // Object[] selected = getSelectedValues();
 356    // ArrayList<ItemT> l = new ArrayList<ItemT>(selected.length);
 357    // for (Object o: selected) { l.add((ItemT)o); }
 358  1 ArrayList<ItemT> l = new ArrayList<ItemT>(1);
 359  1 l.add((ItemT)getSelectedValue());
 360  1 return l;
 361    }
 362   
 363    /** Returns true if the root is selected. Only runs in event thread. */
 364  0 public boolean isRootSelected() { return false; }
 365   
 366    /** @return true if the INavigatorItem is in the selected group, if a group is selected. */
 367  0 public boolean isSelectedInGroup(ItemT i) { return false; }
 368   
 369  0 public void addTopLevelGroup(String name, INavigatorItemFilter<? super ItemT> f) { /* noop */ }
 370   
 371  0 public boolean isTopLevelGroupSelected() { return false; }
 372   
 373    /** Returns the names of the top level groups that the selected items descend from.
 374    * Always throws a GroupNotSelectedException for JListSortNavigator
 375    */
 376  0 public java.util.Set<String> getNamesOfSelectedTopLevelGroup() throws GroupNotSelectedException{
 377  0 throw new GroupNotSelectedException("A top level group is not selected");
 378    }
 379   
 380    /** Since in the JListNavigator it is impossible to select anything but an INavigatorItem,
 381    * this method doesn't need to do anything. See JTreeSortNavigator and IDocumentNavigator.
 382    */
 383  0 public void requestSelectionUpdate(ItemT doc) { /* nothing */ }
 384   
 385    // /** Notify this ListModel that doc has changed and may need updating (if it has changed
 386    // * from modified to unmodified). Should only be performed in the event thread
 387    // */
 388    // public void activeDocumentModified() {
 389    // synchronized(_model) {
 390    // int current = _currentIndex;
 391    // fireSelectionValueChanged(current, current, false);
 392    // }
 393    // }
 394    //
 395  0 public String toString() { synchronized(_model) { return _model.toString(); } }
 396   
 397    /** The cell renderer for this list. */
 398    private static class CustomListCellRenderer extends DefaultListCellRenderer {
 399   
 400    /** Rreturns the renderer component for a cell
 401    * @param list
 402    * @param value
 403    * @param index
 404    * @param isSelected
 405    * @param hasFocus
 406    */
 407  1143 public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean hasFocus) {
 408   
 409  1143 super.getListCellRendererComponent(list, value, index, isSelected, hasFocus);
 410  1143 setText(((INavigatorItem)value).getName());
 411  1143 return this;
 412    }
 413    }
 414   
 415    /** Marks the next selection change as model-initiated (true) or user-initiated (false; default). */
 416  881 public void setNextChangeModelInitiated(boolean b) {
 417  881 putClientProperty(MODEL_INITIATED_PROPERTY_NAME, b?Boolean.TRUE:null);
 418    }
 419   
 420    /** @return whether the next selection change is model-initiated (true) or user-initiated (false). */
 421  673 public boolean isNextChangeModelInitiated() {
 422  673 return getClientProperty(MODEL_INITIATED_PROPERTY_NAME) != null;
 423    }
 424    }