Clover coverage report - DrJava Test Coverage (drjava-20120422-r5456)
Coverage timestamp: Sun Apr 22 2012 03:13:25 CDT
file stats: LOC: 292   Methods: 18
NCLOC: 115   Classes: 2
 
 Source file Conditionals Statements Methods TOTAL
HighlightManager.java 45% 73% 83.3% 69.3%
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.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    private JTextComponent _component;
 67   
 68    /** Constructor
 69    * @param jtc the component whose document will have positions created therein.
 70    */
 71  412 public HighlightManager(JTextComponent jtc) {
 72  412 _component = jtc;
 73  412 _highlights = new Vector<Stack<HighlightInfo>>();
 74    }
 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  3 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  36 public synchronized HighlightInfo addHighlight(int startOffset, int endOffset, Highlighter.HighlightPainter p) {
 94   
 95  36 HighlightInfo newLite = new HighlightInfo(startOffset,endOffset,p);
 96   
 97    // Utilities.showDebug("Adding highlight from " + startOffset + " to " + endOffset);
 98  36 Stack<HighlightInfo> lineStack = _getStackAt(newLite);
 99   
 100  36 if (lineStack != null) {
 101  2 int searchResult = lineStack.search(newLite);
 102  2 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  34 lineStack = new Stack<HighlightInfo>();
 110  34 _highlights.add(lineStack);
 111    }
 112   
 113  34 try {
 114  34 Object highlightTag = _component.getHighlighter().addHighlight(startOffset,endOffset,p);
 115  34 newLite.setHighlightTag(highlightTag);
 116  34 lineStack.push(newLite);
 117  34 return newLite;
 118    }
 119    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  66 private Stack<HighlightInfo> _getStackAt(HighlightInfo h) {
 134   
 135  66 for (Stack<HighlightInfo> stack : _highlights) {
 136  35 if (stack.get(0).matchesRegion(h)) {
 137  32 return stack;
 138    }
 139    }
 140    //if here, no corresponding stack, so return null
 141  34 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  2 public synchronized void removeHighlight(int startOffset, int endOffset, Highlighter.HighlightPainter p) {
 150  2 HighlightInfo newLite = new HighlightInfo(startOffset, endOffset, p);
 151  2 removeHighlight(newLite);
 152    }
 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  30 public void removeHighlight(HighlightInfo newLite) {
 158   
 159   
 160    // int startOffset = newLite.getStartOffset();
 161    // int endOffset = newLite.getEndOffset();
 162   
 163  30 Stack<HighlightInfo> lineStack = _getStackAt(newLite);
 164   
 165  30 if (lineStack== null) {
 166    //System.out.println("Error! No stack to access in region from " + startOffset+ " to " + endOffset);
 167  0 return;
 168    }
 169   
 170  30 int searchResult = lineStack.search(newLite);
 171    //System.out.println("searchResult: " + searchResult);
 172   
 173  30 if (searchResult == 1) {
 174  30 HighlightInfo liteToRemove = lineStack.pop();
 175  30 _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  30 if (lineStack.isEmpty()) {
 185    //System.out.println("Removing empty stack...");
 186    //remove the lineStack
 187  30 _highlights.remove(lineStack);
 188    }
 189   
 190    }
 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  42 public HighlightInfo(int from, int to, Highlighter.HighlightPainter p) {
 208   
 209  42 _highlightTag = null;
 210  42 try {
 211  42 _startPos = _component.getDocument().createPosition(from);
 212  42 _endPos = _component.getDocument().createPosition(to);
 213    }
 214  0 catch (BadLocationException ble) { throw new UnexpectedException(ble); }
 215   
 216  42 _painter = p;
 217    }
 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  34 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  35 public boolean equals(Object o) {
 230   
 231  0 if (o == null || ! (o instanceof HighlightInfo)) return false; // subclasses are defined
 232   
 233   
 234  35 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  35 boolean result = getStartOffset() == hi.getStartOffset() &&
 241    getEndOffset() == hi.getEndOffset() &&
 242    getPainter() == hi.getPainter();
 243   
 244    //System.out.println("HighlightInfo.equals() = " + result);
 245  35 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  28 public void remove() { removeHighlight(this); }
 252   
 253    /** Accessor for the highlight tag.
 254    * @return the highlight tag (which may be null)
 255    */
 256  30 public Object getHighlightTag() { return _highlightTag; }
 257   
 258    /** Accessor for the painter
 259    * @return the painter
 260    */
 261  68 public Highlighter.HighlightPainter getPainter() { return _painter; }
 262   
 263    /** Accessor for the starting offset of this highlight
 264    * @return the start offset
 265    */
 266  140 public int getStartOffset() { return _startPos.getOffset(); }
 267   
 268    /** Accessor for the ending offset of this highlight
 269    * @return the end offset
 270    */
 271  140 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  35 public boolean matchesRegion(HighlightInfo h) {
 278  35 return (getStartOffset() == h.getStartOffset() && getEndOffset() == h.getEndOffset());
 279    }
 280   
 281    /** Refreshes this HighlightInfo object, obtaining a new Highlighter. */
 282  0 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    }
 291    }
 292    }