Clover coverage report -
Coverage timestamp: Sun Apr 18 2004 21:32:30 EDT
file stats: LOC: 333   Methods: 15
NCLOC: 147   Classes: 3
 
 Source file Conditionals Statements Methods TOTAL
SyntaxPane.java 7.1% 41.5% 33.3% 36%
coverage coverage
 1   
 /*
 2   
  * SimplyHTML, a word processor based on Java, HTML and CSS
 3   
  * Copyright (C) 2002, 2003 Ulrich Hilger
 4   
  *
 5   
  * This program is free software; you can redistribute it and/or
 6   
  * modify it under the terms of the GNU General Public License
 7   
  * as published by the Free Software Foundation; either version 2
 8   
  * of the License, or (at your option) any later version.
 9   
  *
 10   
  * This program is distributed in the hope that it will be useful,
 11   
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 12   
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13   
  * GNU General Public License for more details.
 14   
  *
 15   
  * You should have received a copy of the GNU General Public License
 16   
  * along with this program; if not, write to the Free Software
 17   
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 18   
  */
 19   
 
 20   
 import javax.swing.*;
 21   
 import javax.swing.event.*;
 22   
 import javax.swing.text.*;
 23   
 import javax.swing.plaf.*;
 24   
 import java.util.*;
 25   
 import java.util.regex.*;
 26   
 import java.awt.*;
 27   
 import java.awt.event.*;
 28   
 
 29   
 /**
 30   
  * An editor pane with syntax highlighting for HTML tags.
 31   
  *
 32   
  * <p>This is a basic approach to syntax highlighting
 33   
  * using regular expressions. It is used to make the plain
 34   
  * HTML view of application SimplyHTML more legible by
 35   
  * separating tags and attributes from content.</p>
 36   
  *
 37   
  * <p>The method doing the actual highlighting work
 38   
  * is put into an implementation of the Runnable interface
 39   
  * so that it can be called without conflicts.</p>
 40   
  *
 41   
  * <p>Can be refined in the way Patterns are set up, i.e.
 42   
  * not hard wire pattern setup and a GUI to let the
 43   
  * use choose styles for patterns.</p>
 44   
  *
 45   
  * <p>Recommended readings:<br>
 46   
  * 'Regular Expressions and the JavaTM Programming Language' at<br>
 47   
  * <a href="http://developer.java.sun.com/developer/technicalArticles/releases/1.4regex/" target="_blank">http://developer.java.sun.com/developer/technicalArticles/releases/1.4regex/</a><br>
 48   
  * and<br>
 49   
  * Presentation slides 'Rich Clients for Web Services' from JavaOne 2002 at<br>
 50   
  * <a href="http://servlet.java.sun.com/javaone/resources/content/sf2002/conf/sessions/pdfs/2274.pdf" target="_blank">http://servlet.java.sun.com/javaone/resources/content/sf2002/conf/sessions/pdfs/2274.pdf</a>
 51   
  * </p>
 52   
  *
 53   
  * @author Ulrich Hilger
 54   
  * @author Light Development
 55   
  * @author <a href="http://www.lightdev.com">http://www.lightdev.com</a>
 56   
  * @author <a href="mailto:info@lightdev.com">info@lightdev.com</a>
 57   
  * @author published under the terms and conditions of the
 58   
  *      GNU General Public License,
 59   
  *      for details see file gpl.txt in the distribution
 60   
  *      package of this software
 61   
  *
 62   
  * @version stage 11, April 27, 2003
 63   
  *
 64   
  */
 65   
 public class SyntaxPane extends JEditorPane implements CaretListener {
 66   
 
 67   
   /**
 68   
    * Creates a new <code>SyntaxPane</code>.
 69   
    */
 70  79
   public SyntaxPane() {
 71  79
     super();
 72  79
     setEditorKit(new StyledEditorKit());
 73  79
     setupPatterns();
 74   
   }
 75   
 
 76   
   /**
 77   
    * set up HTML patterns and attributes
 78   
    */
 79  79
   private void setupPatterns() {
 80   
 
 81  79
     Pattern p;
 82  79
     SimpleAttributeSet set;
 83  79
     patterns = new Vector();
 84   
 
 85   
     // content text
 86  79
     p = Pattern.compile("\\b\\w+");
 87  79
     set = new SimpleAttributeSet();
 88  79
     StyleConstants.setForeground(set, Color.BLACK);
 89  79
     StyleConstants.setBold(set, false);
 90  79
     patterns.addElement(new RegExStyle(p, set));
 91   
 
 92   
     // a tag
 93  79
     p = Pattern.compile("<[/a-zA-Z0-9\\s]+");
 94  79
     set = new SimpleAttributeSet();
 95  79
     StyleConstants.setForeground(set, new Color(0, 0, 128));
 96  79
     StyleConstants.setBold(set, true);
 97  79
     patterns.addElement(new RegExStyle(p, set));
 98   
 
 99   
     // a tag end
 100  79
     p = Pattern.compile(">");
 101  79
     patterns.addElement(new RegExStyle(p, set));
 102   
 
 103   
     // an attribute
 104  79
     p = Pattern.compile("\\s[/a-zA-Z0-9]+=");
 105  79
     set = new SimpleAttributeSet();
 106  79
     StyleConstants.setForeground(set, new Color(158, 119, 0));
 107  79
     StyleConstants.setBold(set, true);
 108  79
     patterns.addElement(new RegExStyle(p, set));
 109   
 
 110   
     // attribute values
 111  79
     p = Pattern.compile("\"[\\x2D;:/.%#?=,\\w\\s]+\"");
 112  79
     set = new SimpleAttributeSet();
 113  79
     StyleConstants.setForeground(set, Color.BLUE);
 114  79
     StyleConstants.setBold(set, false);
 115  79
     patterns.addElement(new RegExStyle(p, set));
 116   
   }
 117   
 
 118   
   /**
 119   
    * apply syntax highlighting to all HTML tags found in the given
 120   
    * area of the given document
 121   
    *
 122   
    * @param doc  the document to apply syntax highlighting to
 123   
    * @param offset  the position inside the given document to start to apply syntax highlighting to
 124   
    * @param len  the number of characters to apply syntax highlighting to
 125   
    */
 126  0
   public void setMarks(StyledDocument sDoc, int offset, int len) {
 127  0
     SwingUtilities.invokeLater(new StyleUpdater(this, sDoc, offset, len));
 128   
   }
 129   
 
 130   
   /**
 131   
    * overridden from JEditorPane
 132   
    * to suppress line wraps
 133   
    *
 134   
    * @see setSize
 135   
    */
 136  1542
   public boolean getScrollableTracksViewportWidth() {
 137  1542
     return false;
 138   
   }
 139   
 
 140   
   /**
 141   
    * overridden from JEditorPane
 142   
    * to suppress line wraps
 143   
    *
 144   
    * @see getScrollableTracksViewportWidth
 145   
    */
 146  293
   public void setSize(Dimension d) {
 147  293
     if(d.width < getParent().getSize().width) {
 148  293
       d.width = getParent().getSize().width;
 149   
     }
 150  293
     super.setSize(d);
 151   
   }
 152   
 
 153   
   /**
 154   
    * display a wait cursor for lengthy operations
 155   
    */
 156  0
   private void cursor() {
 157  0
     Component gp = getRootPane().getGlassPane();
 158  0
     if(!gp.isVisible()) {
 159  0
       gp.setCursor(waitCursor);
 160  0
       gp.setVisible(true);
 161   
     }
 162   
     else {
 163  0
       gp.setVisible(false);
 164   
     }
 165   
   }
 166   
 
 167   
   /**
 168   
    * StyleUpdater does the actual syntax highlighting work
 169   
    * and can be used in SwingUtilities.invokeLater()
 170   
    */
 171   
   private class StyleUpdater implements Runnable {
 172   
 
 173   
     /**
 174   
      * construct a <code>StyleUpdater</code>
 175   
      *
 176   
      * @param sp  the SyntaxPane this StyleUpdater works on
 177   
      * @param doc  the document to apply syntax highlighting to
 178   
      * @param offset  the position inside the given document to start to apply syntax highlighting to
 179   
      * @param len  the number of characters to apply syntax highlighting to
 180   
      */
 181  0
     public StyleUpdater(SyntaxPane sp, StyledDocument doc, int offset, int len) {
 182  0
       this.sDoc = doc;
 183  0
       this.offset = offset;
 184  0
       this.len = len;
 185  0
       this.sp = sp;
 186   
     }
 187   
 
 188   
     /**
 189   
      * apply snytax highlighting
 190   
      */
 191  0
     public void run() {
 192  0
       Matcher m;
 193  0
       RegExStyle style;
 194  0
       cursor();
 195  0
       sp.removeCaretListener(sp);
 196  0
       try {
 197  0
         int length = sDoc.getLength();
 198  0
         if(length > 0 && len > 0) {
 199  0
           String text = sDoc.getText(offset, len);
 200  0
           if (text != null && text.length() > 0) {
 201  0
             Enumeration pe = patterns.elements();
 202  0
             while (pe.hasMoreElements()) {
 203  0
               style = (RegExStyle) pe.nextElement();
 204  0
               m =  style.getPattern().matcher(text);
 205  0
               while (m.find()) {
 206  0
                 sDoc.setCharacterAttributes(offset + m.start(), m.end() - m.start(), style.getStyle(), true);
 207   
               }
 208   
             }
 209   
           }
 210   
         }
 211   
       }
 212   
       catch (Exception ex) {
 213  0
         System.out.println("StyleUpdater ERROR: " + ex.getMessage());
 214   
       }
 215  0
       sp.addCaretListener(sp);
 216  0
       cursor();
 217   
     }
 218   
 
 219   
     /** the document to apply syntax highlighting to */
 220   
     private StyledDocument sDoc;
 221   
 
 222   
     /** the position inside the given document to start to apply syntax highlighting to */
 223   
     private int offset;
 224   
 
 225   
     /** the number of characters to apply syntax highlighting to */
 226   
     private int len;
 227   
 
 228   
     /** the SyntaxPane this StyleUpdater works on */
 229   
     private SyntaxPane sp;
 230   
   }
 231   
 
 232   
   /**
 233   
    * CaretListener implementation
 234   
    *
 235   
    * <p>updates syntax highlighting for the current line
 236   
    * when the caret moves</p>
 237   
    */
 238  0
   public void caretUpdate(CaretEvent e) {
 239  0
     try {
 240  0
       StyledDocument sDoc = (StyledDocument) getDocument();
 241  0
       int cPos = e.getDot();
 242  0
       int length = sDoc.getLength();
 243  0
       String text = sDoc.getText(0, length);
 244  0
       int lineStart = text.substring(0, cPos).lastIndexOf("\n") + 1;
 245  0
       int lineEnd = text.indexOf("\n", cPos);
 246  0
       if(lineEnd < 0) {
 247  0
         lineEnd = length;
 248   
       }
 249  0
       setMarks(sDoc, lineStart, lineEnd - lineStart);
 250   
     }
 251   
     catch(Exception ex) {}
 252   
   }
 253   
 
 254   
   /**
 255   
    * overridden to keep caret changes during the initial text load
 256   
    * from triggering syntax highlighting repetitively
 257   
    */
 258  0
   public void setText(String t) {
 259  0
     removeCaretListener(this);
 260  0
     super.setText(t);
 261  0
     StyledDocument sDoc = (StyledDocument) getDocument();
 262  0
     setMarks(sDoc, 0, sDoc.getLength());
 263  0
     setCaretPosition(0);
 264  0
     addCaretListener(this);
 265   
   }
 266   
 
 267   
   /**
 268   
    * convenience class associating a pattern with a
 269   
    * set of attributes
 270   
    */
 271   
   public class RegExStyle {
 272   
 
 273   
     /**
 274   
      * construct a <code>RegExStyle</code> instance
 275   
      *
 276   
      * @param p  the <code>Pattern</code> to apply this style to
 277   
      * @param a  the attributes making up this style
 278   
      */
 279  395
     public RegExStyle(Pattern p, AttributeSet a) {
 280  395
       this.p = p;
 281  395
       this.a = a;
 282   
     }
 283   
 
 284   
     /**
 285   
      * get the <code>Pattern</code> this style is to be applied to
 286   
      *
 287   
      * @return the Pattern
 288   
      */
 289  0
     public Pattern getPattern() {
 290  0
       return p;
 291   
     }
 292   
 
 293   
     /**
 294   
      * get the attributes making up this style
 295   
      *
 296   
      * @return the set of attributes
 297   
      */
 298  0
     public AttributeSet getStyle() {
 299  0
       return a;
 300   
     }
 301   
 
 302   
     /**
 303   
      * set the Pattern to apply a given set of attributes to
 304   
      *
 305   
      * @param p  the Pattern
 306   
      */
 307  0
     public void setPattern(Pattern p) {
 308  0
       this.p = p;
 309   
     }
 310   
 
 311   
     /**
 312   
      * set the set of attributes to apply to a given Pattern
 313   
      *
 314   
      * @param a  the set of attributes to use
 315   
      */
 316  0
     public void setStyle(AttributeSet a) {
 317  0
       this.a = a;
 318   
     }
 319   
 
 320   
     /** the Pattern to apply this style to */
 321   
     private Pattern p;
 322   
 
 323   
     /** the attributes making up this style */
 324   
     private AttributeSet a;
 325   
   }
 326   
 
 327   
   /** Patterns registered with this SnytaxPane */
 328   
   private Vector patterns;
 329   
 
 330   
   /** the cursor to use to indicate a lengthy operation is going on */
 331   
   private Cursor waitCursor = Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR);
 332   
 
 333   
 }