Coverage Report - edu.rice.cs.util.swing.HighlightManager
 
Classes in this File Line Coverage Branch Coverage Complexity
HighlightManager
61%
27/44
35%
7/20
2.333
HighlightManager$HighlightInfo
73%
19/26
50%
7/14
2.333
 
 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.swing;
 38  
 
 39  
 import java.util.Stack;
 40  
 import java.util.Vector;
 41  
 
 42  
 import javax.swing.text.JTextComponent;
 43  
 import javax.swing.text.Highlighter;
 44  
 import javax.swing.text.BadLocationException;
 45  
 import javax.swing.text.Position;
 46  
 
 47  
 import edu.rice.cs.util.UnexpectedException;
 48  
 
 49  
 import static edu.rice.cs.plt.object.ObjectUtil.hash;
 50  
 
 51  
 /** This class has synchronized public methods because it is accessed outside of the event thread. */
 52  
 public class HighlightManager {
 53  
   
 54  
   //private Hashtable<HighlightPosition, Stack<HighlightInfo>> _highlights;
 55  
   
 56  
   /** An unsorted Vector of Stack<HighlightInfo>, each of which corresponds to a unique
 57  
     * region in the document. All HighlightInfo objects within a given stack must correspond
 58  
     * to the same region but must have unique Highlighter.HighlightPainters.
 59  
     * Each stack is ordered so the most recent highlight is at the top.
 60  
     */
 61  
   private Vector<Stack<HighlightInfo>> _highlights;
 62  
   
 63  
   /** The component necessary for creating positions in in the document, which is also
 64  
     * contained within this component.
 65  
     */
 66  2322852
   private JTextComponent _component;
 67  
   
 68  
   /** Constructor
 69  
     * @param jtc the component whose document will have positions created therein.
 70  
     */
 71  509248005
   public HighlightManager(JTextComponent jtc) {
 72  509248005
     _component = jtc;
 73  509248005
     _highlights = new Vector<Stack<HighlightInfo>>();
 74  509248005
   }
 75  
   
 76  
   /** Overrides to toString() to support unit testing */
 77  
   
 78  0
   public String toString() { return "HighLightManager(" + _highlights + ")"; }
 79  
   
 80  
   /** Size of highlight stack; used only for unit testing */
 81  
   
 82  0
   public int size() { return _highlights.size(); }
 83  
   
 84  
   /** Adds a highlight using the supplied painter to the vector element(Stack) that exactly corresponds to the 
 85  
     * specified bounds. The most recently added highlights over a given range appear on top of the older highlights. 
 86  
     * All highlights in a given range(Stack) must be unique, that is, each must use a different painter--redundant 
 87  
     * highlights are shifted to the top of the stack, but not added twice.
 88  
     * @param startOffset the offset at which the highlight is to begin.
 89  
     * @param endOffset the offset at which the highlight is to end.
 90  
     * @param p the Highlighter.HighlightPainter for painting
 91  
     * @return HighlightInfo the HighlightInfo object, for keeping a tag of a given highlight
 92  
     */
 93  
   public synchronized HighlightInfo addHighlight(int startOffset, int endOffset, Highlighter.HighlightPainter p) {
 94  
     
 95  1161426
     HighlightInfo newLite = new HighlightInfo(startOffset,endOffset,p);
 96  
     
 97  
 //      Utilities.showDebug("Adding highlight from " + startOffset + " to " + endOffset);
 98  1161426
     Stack<HighlightInfo> lineStack = _getStackAt(newLite);
 99  
     
 100  1161426
     if (lineStack != null) {
 101  0
       int searchResult = lineStack.search(newLite);
 102  0
       if (searchResult == 1) return lineStack.peek();
 103  0
       if (searchResult > 1) {
 104  0
         lineStack.remove(newLite);
 105  
       }
 106  
     }
 107  
     else {
 108  
       //add a new Stack to the empty place in the hashtable
 109  1161426
       lineStack = new Stack<HighlightInfo>();
 110  1161426
        _highlights.add(lineStack);
 111  
     }
 112  
     
 113  
     try {
 114  1161426
       Object highlightTag = _component.getHighlighter().addHighlight(startOffset,endOffset,p);
 115  1161426
       newLite.setHighlightTag(highlightTag);
 116  1161426
       lineStack.push(newLite);
 117  1161426
       return newLite;
 118  
     }
 119  0
     catch (BadLocationException ble) {
 120  
       //if adding a highlight failed, remove any empty stack
 121  0
       if (lineStack.isEmpty()) {
 122  0
         _highlights.remove(lineStack);
 123  
       }
 124  0
       throw new UnexpectedException(ble);
 125  
     }
 126  
   }
 127  
   
 128  
   /** Returns the Stack corresponding to the given region in the document, or null in none exists. ASSUMES that every 
 129  
     * Stack in the vector has a unique region.
 130  
     * @param h  the descriptor for the desired region.
 131  
     * @return  the corresponding Stack, or null
 132  
     */
 133  
   private Stack<HighlightInfo> _getStackAt(HighlightInfo h) {
 134  
     
 135  2745308
     for (Stack<HighlightInfo> stack : _highlights) {
 136  211228
       if (stack.get(0).matchesRegion(h)) {
 137  211228
         return stack;
 138  
       }
 139  
     }
 140  
     //if here, no corresponding stack, so return null
 141  1161426
     return null;
 142  
   }
 143  
   
 144  
   /** Removes a highlight with the specified start/end offsets and the given painter.
 145  
     * @param startOffset the offset at which the desired highlight should start.
 146  
     * @param endOffset the offset at which the desired highlight shoud end.
 147  
     * @param p the Highlighter.HighlightPainter for painting
 148  
     */
 149  
   public synchronized void removeHighlight(int startOffset, int endOffset, Highlighter.HighlightPainter p) {
 150  0
     HighlightInfo newLite = new HighlightInfo(startOffset, endOffset, p);
 151  0
     removeHighlight(newLite);
 152  0
   }
 153  
   
 154  
   /** Removes a given highlight (HighlightInfo) from the highlighter
 155  
     * @param newLite the HighlightInfo object corresponding to the highlight needed to be removed
 156  
     */
 157  
   public void removeHighlight(HighlightInfo newLite) {
 158  
     
 159  
     
 160  
 //      int startOffset = newLite.getStartOffset();
 161  
 //      int endOffset = newLite.getEndOffset();
 162  
     
 163  211228
     Stack<HighlightInfo> lineStack = _getStackAt(newLite);
 164  
     
 165  211228
     if (lineStack== null) {
 166  
       //System.out.println("Error! No stack to access in region from " + startOffset+ " to " +  endOffset);
 167  0
       return;
 168  
     }
 169  
     
 170  211228
     int searchResult = lineStack.search(newLite);
 171  
     //System.out.println("searchResult: " + searchResult);
 172  
     
 173  211228
     if (searchResult == 1) {
 174  211228
       HighlightInfo liteToRemove = lineStack.pop();
 175  211228
       _component.getHighlighter().removeHighlight(liteToRemove.getHighlightTag());
 176  
       //System.out.println("Removed highlight @ " + startOffset);
 177  
     }
 178  0
     else if (searchResult > 1) {
 179  
       //System.out.println("Removing old instance...");
 180  0
       lineStack.remove(newLite);
 181  0
       _component.getHighlighter().removeHighlight(newLite.getHighlightTag());
 182  
     }
 183  
     
 184  211228
     if (lineStack.isEmpty()) {
 185  
       //System.out.println("Removing empty stack...");
 186  
       //remove the lineStack
 187  211228
       _highlights.remove(lineStack);
 188  
     }
 189  
     
 190  211228
   }
 191  
   
 192  
   /** The public inner class defining a "smart" highlight, which can return the value of its start and end
 193  
     * offsets for comparison with other highlights. Also keeps a tag to its actual highlight in the
 194  
     * component's highlighter for easy removal.
 195  
     */
 196  
   public class HighlightInfo {
 197  
     private Object _highlightTag;
 198  
     private Position _startPos;
 199  
     private Position _endPos;
 200  
     private Highlighter.HighlightPainter _painter;
 201  
     
 202  
     /** Constructor takes the bounds and the painter for a highlighter
 203  
       * @param from the offset at which the new highlight will start.
 204  
       * @param to the offset at which the new highlight will end.
 205  
       * @param p the Highlighter.HighlightPainter for painting
 206  
       */
 207  1161426
     public HighlightInfo(int from, int to, Highlighter.HighlightPainter p) {
 208  
       
 209  1161426
       _highlightTag = null;
 210  
       try {
 211  1161426
         _startPos = _component.getDocument().createPosition(from);
 212  1161426
         _endPos = _component.getDocument().createPosition(to);
 213  
       }
 214  0
       catch (BadLocationException ble) { throw new UnexpectedException(ble); }
 215  
       
 216  1161426
       _painter = p;
 217  1161426
     }
 218  
     
 219  
     /** Set the highlight tag for later access to the highlight as it is stored in the components highlighter.
 220  
       * @param highlightTag the Object for keeping track of a stored highlight
 221  
       */
 222  1161426
     public void setHighlightTag ( Object highlightTag) { _highlightTag = highlightTag; }
 223  
     
 224  
     /** Tests equivalency of one HighlightInfo object with this HighlightInfo object. Compares start
 225  
       * and end offsets, and the Highlighter.HighlightPainter -- returns true, if they are the same in both.
 226  
       * @param o the other HighlightInfo object to compare to this one.
 227  
       * @return boolean true, if equivalent; false otherwise.
 228  
       */
 229  
     public boolean equals(Object o) {
 230  
       
 231  211228
       if (o == null || ! (o instanceof HighlightInfo)) return false;  // subclasses are defined
 232  
       
 233  
         
 234  211228
       HighlightInfo hi = (HighlightInfo)o;
 235  
       /*
 236  
        //System.out.println("p0: " + p0 + "  obj.p0: " + obj.p0);
 237  
        //System.out.println("p1: " + p1 + "  obj.p1: " + obj.p1);
 238  
        //System.out.println("p: " + p + "  obj.p: " + obj.p);
 239  
        */
 240  422456
       boolean result = getStartOffset() == hi.getStartOffset() && 
 241  211228
         getEndOffset() == hi.getEndOffset() &&
 242  211228
         getPainter() == hi.getPainter();
 243  
       
 244  
       //System.out.println("HighlightInfo.equals() = " + result);
 245  211228
       return result;
 246  
     }
 247  
     
 248  
     /** Overrides hashCode() for consistency with override of equals(...)  */
 249  0
     public int hashCode() { return hash(getPainter(), getStartOffset(), getEndOffset()); }
 250  
     
 251  211228
     public void remove() { removeHighlight(this); }
 252  
     
 253  
     /** Accessor for the highlight tag.
 254  
       * @return the highlight tag (which may be null)
 255  
       */
 256  211228
     public Object getHighlightTag() { return _highlightTag; }
 257  
     
 258  
     /** Accessor for the painter
 259  
       * @return the painter
 260  
       */
 261  422456
     public Highlighter.HighlightPainter getPainter() { return _painter; }
 262  
     
 263  
     /** Accessor for the starting offset of this highlight
 264  
       * @return the start offset
 265  
       */
 266  844912
     public int getStartOffset() { return _startPos.getOffset(); }
 267  
     
 268  
     /** Accessor for the ending offset of this highlight
 269  
       * @return the end offset
 270  
       */
 271  844912
     public int getEndOffset() { return _endPos.getOffset(); }
 272  
     
 273  
     /** Tests to see if the given offsets correspond to the offsets specified within this highlight.
 274  
       * @param h a HighlightInfo object given the start and end offsets
 275  
       * @return true, if the supplied offsets are the same as those of this highlight.
 276  
       */
 277  
     public boolean matchesRegion(HighlightInfo h) {
 278  211228
       return (getStartOffset() == h.getStartOffset() && getEndOffset() == h.getEndOffset());
 279  
     }
 280  
     
 281  
     /** Refreshes this HighlightInfo object, obtaining a new Highlighter. */
 282  
     public void refresh(Highlighter.HighlightPainter p ) {
 283  
       
 284  0
       this.remove();
 285  0
       HighlightInfo newHighlight = addHighlight(getStartOffset(), getEndOffset(), p);
 286  
       
 287  0
       _painter = p;
 288  
       // turn this HighlightInfo object into the newHighlight
 289  0
       _highlightTag = newHighlight.getHighlightTag();
 290  0
     }
 291  
   }
 292  
 }