Clover coverage report - DrJava Test Coverage (drjava-20100718-r5321)
Coverage timestamp: Sun Jul 18 2010 03:26:51 CDT
file stats: LOC: 411   Methods: 16
NCLOC: 243   Classes: 3
 
 Source file Conditionals Statements Methods TOTAL
ColoringGlyphPainter.java 25% 35.2% 56.2% 35%
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.drjava.model.definitions;
 38   
 39    import javax.swing.text.*;
 40    import java.awt.*;
 41    // TODO: Check synchronization.
 42    import java.util.ArrayList;
 43   
 44    import edu.rice.cs.drjava.DrJava;
 45    import edu.rice.cs.drjava.model.*;
 46    import edu.rice.cs.drjava.model.repl.InteractionsDJDocument;
 47    import edu.rice.cs.drjava.config.OptionConstants;
 48    import edu.rice.cs.drjava.config.OptionEvent;
 49    import edu.rice.cs.drjava.config.OptionListener;
 50    import edu.rice.cs.drjava.model.definitions.reducedmodel.*;
 51   
 52    // import edu.rice.cs.util.swing.Utilities; // conflicts with javax.swing.text.Utilities
 53   
 54   
 55    public class ColoringGlyphPainter extends GlyphView.GlyphPainter implements OptionConstants {
 56   
 57    public static Color COMMENTED_COLOR = DrJava.getConfig().getSetting(DEFINITIONS_COMMENT_COLOR);
 58    public static Color DOUBLE_QUOTED_COLOR = DrJava.getConfig().getSetting(DEFINITIONS_DOUBLE_QUOTED_COLOR);
 59    public static Color SINGLE_QUOTED_COLOR = DrJava.getConfig().getSetting(DEFINITIONS_SINGLE_QUOTED_COLOR);
 60    public static Color NORMAL_COLOR = DrJava.getConfig().getSetting(DEFINITIONS_NORMAL_COLOR);
 61    public static Color KEYWORD_COLOR = DrJava.getConfig().getSetting(DEFINITIONS_KEYWORD_COLOR);
 62    public static Color NUMBER_COLOR = DrJava.getConfig().getSetting(DEFINITIONS_NUMBER_COLOR);
 63    public static Color TYPE_COLOR = DrJava.getConfig().getSetting(DEFINITIONS_TYPE_COLOR);
 64    public static Font MAIN_FONT = DrJava.getConfig().getSetting(FONT_MAIN);
 65   
 66    //Interactions only colors
 67    public static Color INTERACTIONS_SYSTEM_ERR_COLOR = DrJava.getConfig().getSetting(SYSTEM_ERR_COLOR);
 68    public static Color INTERACTIONS_SYSTEM_IN_COLOR = DrJava.getConfig().getSetting(SYSTEM_IN_COLOR);
 69    public static Color INTERACTIONS_SYSTEM_OUT_COLOR = DrJava.getConfig().getSetting(SYSTEM_OUT_COLOR);
 70    //Renamed as to avoid confusion with the one in option constants
 71    public static Color ERROR_COLOR = DrJava.getConfig().getSetting(INTERACTIONS_ERROR_COLOR);
 72    public static Color DEBUGGER_COLOR = DrJava.getConfig().getSetting(DEBUG_MESSAGE_COLOR);
 73   
 74    private boolean _listenersAttached;
 75    private Runnable _lambdaRepaint;
 76    private FontMetrics _metrics;
 77   
 78  1546 public ColoringGlyphPainter(Runnable lambdaRepaint) {
 79  1546 _listenersAttached = false;
 80  1546 _lambdaRepaint = lambdaRepaint;
 81    // _metrics is initialized by sync(), which thus must be called before any use of _metrics
 82    }
 83   
 84    /** Paints the glyphs representing the given range. Only runs in the event thread. */
 85  0 public void paint(GlyphView v, Graphics g, Shape a, int start, int end) {
 86   
 87    // If there's nothing to show, don't do anything!
 88    // For some reason I don't understand we tend to get called sometimes to render a zero-length area.
 89  0 if (start == end) return;
 90   
 91  0 sync(v);
 92   
 93    // Might be a PlainDocument (when AbstractDJPane is first constructed).
 94    // See comments for DefinitionsEditorKit.createNewDocument() for details.
 95  0 Document doc = v.getDocument();
 96  0 AbstractDJDocument djdoc = null;
 97  0 if (doc instanceof AbstractDJDocument)
 98  0 djdoc = (AbstractDJDocument) doc;
 99    else
 100  0 return; // return if there is no AbstracDJDocument
 101   
 102  0 Segment text;
 103  0 TabExpander expander = v.getTabExpander();
 104  0 Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a : a.getBounds();
 105   
 106    // determine the x coordinate to render the glyphs
 107  0 int x = alloc.x;
 108  0 int p = v.getStartOffset();
 109  0 if (p != start) {
 110  0 text = v.getText(p, start);
 111  0 int width = Utilities.getTabbedTextWidth(text, _metrics, x, expander, p);
 112  0 x += width;
 113    }
 114   
 115    // determine the y coordinate to render the glyphs
 116  0 int y = alloc.y + _metrics.getHeight() - _metrics.getDescent();
 117   
 118  0 text = v.getText(start, end);
 119   
 120  0 ArrayList<HighlightStatus> stats = djdoc.getHighlightStatus(start, end);
 121  0 if (stats.size() < 1) throw new RuntimeException("GetHighlightStatus returned nothing!");
 122  0 try {
 123  0 for (HighlightStatus stat: stats) {
 124  0 int length = stat.getLength();
 125  0 int location = stat.getLocation();
 126   
 127  0 if (location < end && location + length > start) {
 128   
 129    // Adjust the length and location to fit within the bounds of the element we're about to render
 130  0 if (location < start) {
 131  0 length -= (start-location);
 132  0 location = start;
 133    }
 134  0 if (location + length > end) length = end - location;
 135   
 136  0 if (! (djdoc instanceof InteractionsDJDocument) ||
 137    ! ((InteractionsDJDocument)djdoc).setColoring((start+end)/2,g))
 138  0 setFormattingForState(g, stat.getState());
 139   
 140  0 djdoc.getText(location, length, text);
 141  0 x = Utilities.drawTabbedText(text, x, y, g, v.getTabExpander(), location);
 142    }
 143    }
 144    }
 145    catch(BadLocationException ble) { /* don't continue rendering if such an exception is found */ }
 146    }
 147   
 148    /** Determines the span the glyphs given a start location (for tab expansion). Only runs in event thread. */
 149  2310 public float getSpan(GlyphView v, int start, int end, TabExpander e, float x) {
 150  2310 sync(v);
 151  2310 Segment text = v.getText(start, end);
 152  2310 int width = Utilities.getTabbedTextWidth(text, _metrics, (int) x, e, start);
 153  2310 return width;
 154    }
 155   
 156    /** Only runs in event thread. */
 157  1896 public float getHeight(GlyphView v) {
 158  1896 sync(v);
 159  1896 return _metrics.getHeight();
 160    }
 161   
 162    /** Fetches the ascent above the baseline for the glyphs corresponding to the given range in the model. Only runs
 163    * in event thread.
 164    */
 165  948 public float getAscent(GlyphView v) {
 166  948 sync(v);
 167  948 return _metrics.getAscent();
 168    }
 169   
 170    /** Fetches the descent below the baseline for the glyphs corresponding to the given range in the model. Assumes
 171    * ReadLocak is held.
 172    */
 173  948 public float getDescent(GlyphView v) {
 174  948 sync(v);
 175  948 return _metrics.getDescent();
 176    }
 177   
 178    /** Only runs in event thread. */
 179  61 public Shape modelToView(GlyphView v, int pos, Position.Bias bias, Shape a) throws BadLocationException {
 180   
 181  61 sync(v);
 182  61 Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a : a.getBounds();
 183  61 int start = v.getStartOffset();
 184  61 int end = v.getEndOffset();
 185  61 TabExpander expander = v.getTabExpander();
 186  61 Segment text;
 187   
 188  61 if(pos == end) {
 189    // The caller of this is left to right and borders a right to
 190    // left view, return our end location.
 191  0 return new Rectangle(alloc.x + alloc.width, alloc.y, 0,
 192    _metrics.getHeight());
 193    }
 194  61 if ((pos >= start) && (pos <= end)) {
 195    // determine range to the left of the position
 196  61 text = v.getText(start, pos);
 197  61 int width = Utilities.getTabbedTextWidth(text, _metrics, alloc.x, expander, start);
 198  61 return new Rectangle(alloc.x + width, alloc.y, 0, _metrics.getHeight());
 199    }
 200  0 throw new BadLocationException("modelToView - can't convert", end);
 201    }
 202   
 203    /** Provides a mapping from the view coordinate space to the logical coordinate space of the model. Only runs in
 204    * event thread.
 205    * @param v the view containing the view coordinates
 206    * @param x the X coordinate
 207    * @param y the Y coordinate
 208    * @param a the allocated region to render into
 209    * @param biasReturn always returns <code>Position.Bias.Forward</code> as the zero-th element of this array
 210    * @return the location within the model that best represents the given point in the view
 211    * @see View#viewToModel
 212    */
 213  0 public int viewToModel(GlyphView v, float x, float y, Shape a, Position.Bias[] biasReturn) {
 214  0 sync(v);
 215  0 Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a : a.getBounds();
 216  0 int start = v.getStartOffset();
 217  0 int end = v.getEndOffset();
 218  0 TabExpander expander = v.getTabExpander();
 219  0 Segment text = v.getText(start, end);
 220   
 221  0 int offs = Utilities.getTabbedTextOffset(text, _metrics,
 222    alloc.x, (int) x, expander, start);
 223  0 int retValue = start + offs;
 224  0 if(retValue == end) {
 225    // No need to return backward bias as GlyphPainter1 is used for
 226    // ltr text only.
 227  0 retValue--;
 228    }
 229  0 biasReturn[0] = Position.Bias.Forward;
 230  0 return retValue;
 231    }
 232   
 233    /** Determines the best location (in the model) to break the given view. This method attempts to break on a
 234    * whitespace location. If a whitespace location can't be found, the nearest character location is returned.
 235    * Only runs in event thread.
 236    *
 237    * @param v The view
 238    * @param start The location in the model where the fragment should start its representation >= 0
 239    * @param x The graphic location along the axis that the broken view would occupy >= 0; this may be useful for
 240    * things like tab calculations
 241    * @param len Specifies the distance into the view where a potential break is desired >= 0
 242    * @return The model location desired for a break
 243    * @see View#breakView
 244    */
 245  1272 public int getBoundedPosition(GlyphView v, int start, float x, float len) {
 246  1272 sync(v);
 247  1272 TabExpander expander = v.getTabExpander();
 248  1272 Segment s = v.getText(start, v.getEndOffset());
 249  1272 int index = Utilities.getTabbedTextOffset(s, _metrics, (int)x, (int)(x+len), expander, start, false);
 250  1272 int end = start + index;
 251  1272 return end;
 252    }
 253   
 254    /** Only runs in event thread. */
 255  7435 void sync(GlyphView v) {
 256  7435 Font f = v.getFont();
 257  7435 if ((_metrics == null) || (! f.equals(_metrics.getFont()))) {
 258    // fetch a new FontMetrics
 259  204 Toolkit kit;
 260  204 Component c = v.getContainer();
 261  204 if (c != null) {
 262  204 kit = c.getToolkit();
 263    } else {
 264  0 kit = Toolkit.getDefaultToolkit();
 265    }
 266    /* The deprecated method here is necessary to get a handle on a FontMetrics object. This is required by our
 267    * dependence on the javax.swing.text.Utilities class, which does a lot of Java 1.1-style calculation (presumably
 268    * these methods should be deprecated, too). The deprecated use can't be fixed without an in-depth understanding
 269    * of fonts, glyphs, and font rendering. Where _metrics is currently used, the Font methods getLineMetrics,
 270    * getStringBounds, getHeight, getAscent, and getDescent will probably be helpful.
 271    */
 272  204 @SuppressWarnings("deprecation")
 273    FontMetrics newMetrics = kit.getFontMetrics(f);
 274  204 _metrics = newMetrics;
 275    }
 276   
 277  7435 Document doc = v.getDocument();
 278  7435 if (! _listenersAttached && (doc instanceof AbstractDJDocument)) {
 279  204 attachOptionListeners((AbstractDJDocument)doc);
 280    }
 281    }
 282   
 283    /** Given a particular state, assign it a color.
 284    * @param g Graphics object
 285    * @param state a given state
 286    */
 287  0 private void setFormattingForState(Graphics g, int state) {
 288  0 switch (state) {
 289  0 case HighlightStatus.NORMAL:
 290  0 g.setColor(NORMAL_COLOR);
 291  0 break;
 292  0 case HighlightStatus.COMMENTED:
 293  0 g.setColor(COMMENTED_COLOR);
 294  0 break;
 295  0 case HighlightStatus.SINGLE_QUOTED:
 296  0 g.setColor(SINGLE_QUOTED_COLOR);
 297  0 break;
 298  0 case HighlightStatus.DOUBLE_QUOTED:
 299  0 g.setColor(DOUBLE_QUOTED_COLOR);
 300  0 break;
 301  0 case HighlightStatus.KEYWORD:
 302  0 g.setColor(KEYWORD_COLOR);
 303  0 break;
 304  0 case HighlightStatus.NUMBER:
 305  0 g.setColor(NUMBER_COLOR);
 306  0 break;
 307  0 case HighlightStatus.TYPE:
 308  0 g.setColor(TYPE_COLOR);
 309  0 break;
 310  0 default:
 311  0 throw new RuntimeException("Can't get color for invalid state: " + state);
 312    }
 313  0 g.setFont(MAIN_FONT);
 314    }
 315   
 316   
 317    // /** Called when a change occurs.
 318    // * @param changes document changes
 319    // * @param a a Shape
 320    // * @param f a ViewFactory
 321    // */
 322    // public void changedUpdate(DocumentEvent changes, Shape a, ViewFactory f) {
 323    // super.changedUpdate(changes, a, f);
 324    // // Make sure we redraw since something changed in the formatting
 325    // Container c = getContainer();
 326    // if (c != null) c.repaint();
 327    // }
 328   
 329  204 private void attachOptionListeners(AbstractDJDocument doc) {
 330    // Listen for updates to configurable colors
 331  204 final ColorOptionListener col = new ColorOptionListener();
 332  204 final FontOptionListener fol = new FontOptionListener();
 333   
 334    // These events can occur in the event thread, and so can't clear the event queue
 335   
 336    // delete the old color listeners, because they're hanging onto the wrong coloringview
 337    // add color listeners to highlight keywords etc
 338  204 DrJava.getConfig().addOptionListener( OptionConstants.DEFINITIONS_COMMENT_COLOR, col);
 339  204 DrJava.getConfig().addOptionListener( OptionConstants.DEFINITIONS_DOUBLE_QUOTED_COLOR, col);
 340  204 DrJava.getConfig().addOptionListener( OptionConstants.DEFINITIONS_SINGLE_QUOTED_COLOR, col);
 341  204 DrJava.getConfig().addOptionListener( OptionConstants.DEFINITIONS_NORMAL_COLOR, col);
 342  204 DrJava.getConfig().addOptionListener( OptionConstants.DEFINITIONS_KEYWORD_COLOR, col);
 343  204 DrJava.getConfig().addOptionListener( OptionConstants.DEFINITIONS_NUMBER_COLOR, col);
 344  204 DrJava.getConfig().addOptionListener( OptionConstants.DEFINITIONS_TYPE_COLOR, col);
 345  204 DrJava.getConfig().addOptionListener( OptionConstants.FONT_MAIN, fol);
 346   
 347  204 DrJava.getConfig().addOptionListener( OptionConstants.SYSTEM_ERR_COLOR, col);
 348  204 DrJava.getConfig().addOptionListener( OptionConstants.SYSTEM_IN_COLOR, col);
 349  204 DrJava.getConfig().addOptionListener( OptionConstants.SYSTEM_OUT_COLOR, col);
 350  204 DrJava.getConfig().addOptionListener( OptionConstants.INTERACTIONS_ERROR_COLOR, col);
 351  204 DrJava.getConfig().addOptionListener( OptionConstants.DEBUG_MESSAGE_COLOR, col);
 352   
 353    // The listeners that were added in the above lines need to be removed from
 354    // the config framework when the document corresponding to this painter is
 355    // kicked out of the DocumentCache. Otherwise, this painter will remain
 356    // un-garbage-collected and unused.
 357  204 if (doc instanceof DefinitionsDocument) {
 358    // remove the listeners when the document closes
 359  0 ((DefinitionsDocument)doc).addDocumentClosedListener(new DocumentClosedListener() {
 360  0 public void close() {
 361  0 DrJava.getConfig().removeOptionListener( OptionConstants.DEFINITIONS_COMMENT_COLOR, col);
 362  0 DrJava.getConfig().removeOptionListener( OptionConstants.DEFINITIONS_DOUBLE_QUOTED_COLOR, col);
 363  0 DrJava.getConfig().removeOptionListener( OptionConstants.DEFINITIONS_SINGLE_QUOTED_COLOR, col);
 364  0 DrJava.getConfig().removeOptionListener( OptionConstants.DEFINITIONS_NORMAL_COLOR, col);
 365  0 DrJava.getConfig().removeOptionListener( OptionConstants.DEFINITIONS_KEYWORD_COLOR, col);
 366  0 DrJava.getConfig().removeOptionListener( OptionConstants.DEFINITIONS_NUMBER_COLOR, col);
 367  0 DrJava.getConfig().removeOptionListener( OptionConstants.DEFINITIONS_TYPE_COLOR, col);
 368  0 DrJava.getConfig().removeOptionListener( OptionConstants.FONT_MAIN, fol);
 369  0 DrJava.getConfig().removeOptionListener( OptionConstants.SYSTEM_ERR_COLOR, col);
 370  0 DrJava.getConfig().removeOptionListener( OptionConstants.SYSTEM_IN_COLOR, col);
 371  0 DrJava.getConfig().removeOptionListener( OptionConstants.SYSTEM_OUT_COLOR, col);
 372  0 DrJava.getConfig().removeOptionListener( OptionConstants.INTERACTIONS_ERROR_COLOR, col);
 373  0 DrJava.getConfig().removeOptionListener( OptionConstants.DEBUG_MESSAGE_COLOR, col);
 374    }
 375    });
 376    }
 377  204 _listenersAttached = true;
 378    }
 379   
 380    /** Called when an OptionListener perceives a change in any of the colors */
 381  0 public void updateColors() {
 382   
 383  0 COMMENTED_COLOR = DrJava.getConfig().getSetting(DEFINITIONS_COMMENT_COLOR);
 384  0 DOUBLE_QUOTED_COLOR = DrJava.getConfig().getSetting(DEFINITIONS_DOUBLE_QUOTED_COLOR);
 385  0 SINGLE_QUOTED_COLOR = DrJava.getConfig().getSetting(DEFINITIONS_SINGLE_QUOTED_COLOR);
 386  0 NORMAL_COLOR = DrJava.getConfig().getSetting(DEFINITIONS_NORMAL_COLOR);
 387  0 KEYWORD_COLOR = DrJava.getConfig().getSetting(DEFINITIONS_KEYWORD_COLOR);
 388  0 NUMBER_COLOR = DrJava.getConfig().getSetting(DEFINITIONS_NUMBER_COLOR);
 389  0 TYPE_COLOR = DrJava.getConfig().getSetting(DEFINITIONS_TYPE_COLOR);
 390   
 391  0 INTERACTIONS_SYSTEM_ERR_COLOR = DrJava.getConfig().getSetting(SYSTEM_ERR_COLOR);
 392  0 INTERACTIONS_SYSTEM_IN_COLOR = DrJava.getConfig().getSetting(SYSTEM_IN_COLOR);
 393  0 INTERACTIONS_SYSTEM_OUT_COLOR = DrJava.getConfig().getSetting(SYSTEM_OUT_COLOR);
 394  0 ERROR_COLOR = DrJava.getConfig().getSetting(INTERACTIONS_ERROR_COLOR);
 395  0 DEBUGGER_COLOR = DrJava.getConfig().getSetting(DEBUG_MESSAGE_COLOR);
 396   
 397  0 EventQueue.invokeLater(_lambdaRepaint);
 398    }
 399   
 400    /** The OptionListeners for DEFINITIONS COLORs */
 401    private class ColorOptionListener implements OptionListener<Color> {
 402  0 public void optionChanged(OptionEvent<Color> oce) { updateColors(); }
 403    }
 404   
 405    private static class FontOptionListener implements OptionListener<Font> {
 406  0 public void optionChanged(OptionEvent<Font> oce) {
 407  0 MAIN_FONT = DrJava.getConfig().getSetting(FONT_MAIN);
 408    }
 409    }
 410   
 411    }