Clover coverage report -
Coverage timestamp: Sun Apr 18 2004 21:32:30 EDT
file stats: LOC: 407   Methods: 17
NCLOC: 228   Classes: 3
 
 Source file Conditionals Statements Methods TOTAL
SHTMLDocument.java 27.1% 47.8% 76.5% 45%
coverage coverage
 1   
 /*
 2   
  * SimplyHTML, a word processor based on Java, HTML and CSS
 3   
  * Copyright (C) 2002 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.text.html.HTMLDocument;
 21   
 
 22   
 import javax.swing.text.html.*;
 23   
 import javax.swing.text.*;
 24   
 import javax.swing.event.DocumentEvent;
 25   
 import java.net.*;
 26   
 import java.util.*;
 27   
 import javax.swing.undo.*;
 28   
 import javax.swing.event.*;
 29   
 
 30   
 
 31   
 /**
 32   
  * Extends <code>HTMLDocument</code> by a custom reader which supports
 33   
  * the SPAN tag.
 34   
  *
 35   
  * @author Ulrich Hilger
 36   
  * @author Light Development
 37   
  * @author <a href="http://www.lightdev.com">http://www.lightdev.com</a>
 38   
  * @author <a href="mailto:info@lightdev.com">info@lightdev.com</a>
 39   
  * @author published under the terms and conditions of the
 40   
  *      GNU General Public License,
 41   
  *      for details see file gpl.txt in the distribution
 42   
  *      package of this software
 43   
  *
 44   
  * @version stage 11, April 27, 2003
 45   
  */
 46   
 
 47   
 public class SHTMLDocument extends HTMLDocument {
 48   
 
 49   
   private AttributeContext context;
 50   
 
 51   
   /**
 52   
    * Constructs an SHTMLDocument.
 53   
    */
 54  1
   public SHTMLDocument() {
 55  1
     this(new GapContent(BUFFER_SIZE_DEFAULT), new StyleSheet());
 56   
   }
 57   
 
 58   
   /**
 59   
    * Constructs an SHTMLDocument with the default content
 60   
    * storage implementation and the given style/attribute
 61   
    * storage mechanism.
 62   
    *
 63   
    * @param styles  the styles
 64   
    */
 65  238
   public SHTMLDocument(StyleSheet styles) {
 66  238
     this(new GapContent(BUFFER_SIZE_DEFAULT), styles);
 67   
   }
 68   
 
 69   
   /**
 70   
    * Constructs an SHTMLDocument with the given content
 71   
    * storage implementation and the given style/attribute
 72   
    * storage mechanism.
 73   
    *
 74   
    * @param c  the container for the content
 75   
    * @param styles the styles
 76   
    */
 77  239
   public SHTMLDocument(Content c, StyleSheet styles) {
 78  239
     super(c, styles);
 79   
   }
 80   
 
 81   
   /**
 82   
    * apply a set of attributes to a given document element
 83   
    *
 84   
    * @param e  the element to apply attributes to
 85   
    * @param a  the set of attributes to apply
 86   
    */
 87  4
   public void addAttributes(Element e, AttributeSet a) {
 88  4
     if ((e != null) && (a != null)) {
 89  4
       try {
 90  4
         writeLock();
 91   
         //System.out.println("SHTMLDocument addAttributes e=" + e);
 92   
         //System.out.println("SHTMLDocument addAttributes a=" + a);
 93  4
         int start = e.getStartOffset();
 94  4
         DefaultDocumentEvent changes = new DefaultDocumentEvent(start,
 95   
             e.getEndOffset() - start, DocumentEvent.EventType.CHANGE);
 96  4
         AttributeSet sCopy = a.copyAttributes();
 97  4
         MutableAttributeSet attr = (MutableAttributeSet) e.getAttributes();
 98  4
         changes.addEdit(new AttributeUndoableEdit(e, sCopy, false));
 99  4
         attr.addAttributes(a);
 100  4
         changes.end();
 101  4
         fireChangedUpdate(changes);
 102  4
         fireUndoableEditUpdate(new UndoableEditEvent(this, changes));
 103   
       }
 104   
       finally {
 105  4
         writeUnlock();
 106   
       }
 107   
     }
 108   
   }
 109   
 
 110   
   /**
 111   
    * Remove a consecutive group of child Elements
 112   
    *
 113   
    * @param e  the parent element to remove child elements from
 114   
    * @param index  the index of the first child element to remove
 115   
    * @param count  the number of child elements to remove
 116   
    */
 117  0
   public void removeElements(Element e, int index, int count)
 118   
       throws BadLocationException
 119   
   {
 120  0
     writeLock();
 121  0
     int start = e.getElement(index).getStartOffset();
 122  0
     int end = e.getElement(index + count - 1).getEndOffset();
 123  0
     try {
 124  0
       Element[] removed = new Element[count];
 125  0
       Element[] added = new Element[0];
 126  0
       for (int counter = 0; counter < count; counter++) {
 127  0
         removed[counter] = e.getElement(counter + index);
 128   
       }
 129  0
       DefaultDocumentEvent dde = new DefaultDocumentEvent(
 130   
           start, end - start, DocumentEvent.EventType.REMOVE);
 131  0
       ((AbstractDocument.BranchElement)e).replace(index, removed.length,
 132   
           added);
 133  0
       dde.addEdit(new ElementEdit(e, index, removed, added));
 134  0
       UndoableEdit u = getContent().remove(start, end - start);
 135  0
       if (u != null) {
 136  0
         dde.addEdit(u);
 137   
       }
 138  0
       postRemoveUpdate(dde);
 139  0
       dde.end();
 140  0
       fireRemoveUpdate(dde);
 141  0
       if (u != null) {
 142  0
         fireUndoableEditUpdate(new UndoableEditEvent(this, dde));
 143   
       }
 144   
     }
 145   
     finally {
 146  0
       writeUnlock();
 147   
     }
 148   
   }
 149   
 
 150   
   /* ------------------ custom document title handling start -------------------- */
 151   
 
 152   
   /**
 153   
    * set the title of this SHTMLDocument
 154   
    *
 155   
    * @param title  the title this document shall have
 156   
    */
 157  1
   public void setDocumentTitle(String title) {
 158  1
     try {
 159  1
       String titleHTML = "<title></title>";
 160  1
       Element defaultRoot = getDefaultRootElement();
 161  1
       Element head = Util.findElementDown(HTML.Tag.HEAD.toString(), defaultRoot);
 162  1
       if(head != null) {
 163  1
         Element pImpl = Util.findElementDown(HTML.Tag.IMPLIED.toString(), head);
 164  1
         if(pImpl != null) {
 165  1
           Element tElem = Util.findElementDown(HTML.Tag.TITLE.toString(), pImpl);
 166  1
           if(tElem == null) {
 167  1
             insertBeforeEnd(pImpl, titleHTML);
 168   
           }
 169   
         }
 170   
       }
 171   
       else {
 172  0
         Element body = Util.findElementDown(HTML.Tag.BODY.toString(), defaultRoot);
 173  0
         insertBeforeStart(body, "<head>" + titleHTML + "</head>");
 174   
       }
 175  1
       putProperty(Document.TitleProperty, title);
 176   
     }
 177   
     catch(Exception e) {
 178  0
       Util.errMsg(null, "An exception occurred while trying to insert the title", e);
 179   
     }
 180   
   }
 181   
 
 182   
   /**
 183   
    * get the title of this SHTMLDocument
 184   
    *
 185   
    * @return  the title of this document or null if none was set so far
 186   
    */
 187  2
   public String getDocumentTitle() {
 188  2
     Object title = getProperty(Document.TitleProperty);
 189  2
     if(title != null) {
 190  1
       return title.toString();
 191   
     }
 192   
     else {
 193  1
       return null;
 194   
     }
 195   
   }
 196   
 
 197   
   /* ------------------ custom document title handling end -------------------- */
 198   
 
 199   
   /* ------------------ custom style sheet reference handling start -------------------- */
 200   
 
 201   
   /**
 202   
    * insert a style sheet reference into the head of this SHTMLDocument
 203   
    */
 204  1
   public void insertStyleRef() {
 205  1
     try {
 206  1
       String styleRef = "  <link rel=stylesheet type=\"text/css\" href=\"" +
 207   
                         DocumentPane.DEFAULT_STYLE_SHEET_NAME + "\">";
 208  1
       Element defaultRoot = getDefaultRootElement();
 209  1
       Element head = Util.findElementDown(HTML.Tag.HEAD.toString(), defaultRoot);
 210  1
       if(head != null) {
 211  0
         Element pImpl = Util.findElementDown(HTML.Tag.IMPLIED.toString(), head);
 212  0
         if(pImpl != null) {
 213  0
           Element link = Util.findElementDown(HTML.Tag.LINK.toString(), pImpl);
 214  0
           if(link != null) {
 215  0
             setOuterHTML(link, styleRef);
 216   
           }
 217   
           else {
 218  0
             insertBeforeEnd(pImpl, styleRef);
 219   
           }
 220   
         }
 221   
       }
 222   
       else {
 223  1
         Element body = Util.findElementDown(HTML.Tag.BODY.toString(), defaultRoot);
 224  1
         insertBeforeStart(body, "<head>" + styleRef + "</head>");
 225   
       }
 226   
     }
 227   
     catch(Exception e) {
 228  0
       Util.errMsg(null, "An exception occurred while trying to insert the style sheet reference link", e);
 229   
     }
 230   
   }
 231   
 
 232   
   /**
 233   
    * check whether or not this SHTMLDocument has an explicit style sheet reference
 234   
    *
 235   
    * @return true, if a style sheet reference was found, false if not
 236   
    */
 237  157
   public boolean hasStyleRef() {
 238  157
     return (getStyleRef() != null);
 239   
   }
 240   
 
 241   
   /**
 242   
    * get the style sheet reference of the document in this
 243   
    * <code>DocumentPane</code>.
 244   
    *
 245   
    * @return the reference to this document's style sheet or
 246   
    *    null if none is found
 247   
    */
 248  158
   public String getStyleRef() {
 249  158
     String linkName = null;
 250  158
     Element link = Util.findElementDown(HTML.Tag.LINK.toString(),
 251   
                                         getDefaultRootElement());
 252  158
     if(link != null) {
 253  2
       Object href = link.getAttributes().getAttribute(HTML.Attribute.HREF);
 254  2
       if(href != null) {
 255  2
         linkName = href.toString();
 256   
       }
 257   
     }
 258  158
     return linkName;
 259   
   }
 260   
 
 261   
   /* ------------------ custom style sheet reference handling end -------------------- */
 262   
 
 263   
   /* -------- custom reader implementation start ------ */
 264   
 
 265   
   /**
 266   
    * Fetches the reader for the parser to use to load the document
 267   
    * with HTML.  This is implemented to return an instance of
 268   
    * SHTMLDocument.SHTMLReader.
 269   
    */
 270  31
   public HTMLEditorKit.ParserCallback getReader(int pos) {
 271  31
     Object desc = getProperty(Document.StreamDescriptionProperty);
 272  31
     if (desc instanceof URL) {
 273  0
         setBase((URL)desc);
 274   
     }
 275  31
     SHTMLReader reader = new SHTMLReader(pos);
 276  31
     return reader;
 277   
   }
 278   
 
 279   
   /**
 280   
    * This reader extends HTMLDocument.HTMLReader by the capability
 281   
    * to handle SPAN tags
 282   
    */
 283   
   public class SHTMLReader extends HTMLDocument.HTMLReader {
 284   
 
 285   
     /** action needed to handle SPAN tags */
 286   
     SHTMLCharacterAction ca = new SHTMLCharacterAction();
 287   
 
 288   
     /** the attributes found in a STYLE attribute */
 289   
     AttributeSet styleAttributes;
 290   
 
 291   
     /** indicates whether we're inside a SPAN tag */
 292   
     boolean inSpan = false;
 293   
 
 294   
     /**
 295   
      * Constructor
 296   
      */
 297  31
     public SHTMLReader(int offset) {
 298  31
       super(offset, 0, 0, null);
 299   
     }
 300   
 
 301   
     /**
 302   
      * handle a start tag received by the parser
 303   
      *
 304   
      * if it is a SPAN tag, convert the contents of the STYLE
 305   
      * attribute to an AttributeSet and add it to the contents
 306   
      * of this tag.
 307   
      *
 308   
      * Otherwise let HTMLDocument.HTMLReader do the work.
 309   
      */
 310  96
     public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos) {
 311  96
       if(t == HTML.Tag.SPAN) {
 312  0
         if(a.isDefined(HTML.Attribute.STYLE)) {
 313  0
           String decl = (String)a.getAttribute(HTML.Attribute.STYLE);
 314  0
           a.removeAttribute(HTML.Attribute.STYLE);
 315  0
           styleAttributes = getStyleSheet().getDeclaration(decl);
 316  0
           a.addAttributes(styleAttributes);
 317   
         }
 318   
         else {
 319  0
           styleAttributes = null;
 320   
         }
 321  0
         TagAction action = (TagAction) ca;
 322   
 
 323  0
         if (action != null) {
 324   
           /**
 325   
            * remember which part we're in for handleSimpleTag
 326   
            */
 327  0
           inSpan = true;
 328   
 
 329  0
           action.start(t, a);
 330   
         }
 331   
       }
 332   
       else {
 333  96
         super.handleStartTag(t, a, pos);
 334   
       }
 335   
     }
 336   
 
 337   
     /**
 338   
      * SPAN tags are directed to handleSimpleTag by the parser.
 339   
      * If a SPAN tag is detected in this method, it gets redirected
 340   
      * to handleStartTag and handleEndTag respectively.
 341   
      */
 342  0
     public void handleSimpleTag(HTML.Tag t, MutableAttributeSet a, int pos) {
 343  0
       if(t == HTML.Tag.SPAN) {
 344  0
         if(inSpan) {
 345  0
           handleEndTag(t, pos);
 346   
         }
 347   
         else {
 348  0
           handleStartTag(t, a, pos);
 349   
         }
 350   
       }
 351   
       else {
 352  0
         super.handleSimpleTag(t, a, pos);
 353   
       }
 354   
     }
 355   
 
 356   
     /**
 357   
      * If a SPAN tag is directed to this method, end its action,
 358   
      * otherwise, let HTMLDocument.HTMLReader do the work
 359   
      */
 360  96
     public void handleEndTag(HTML.Tag t, int pos) {
 361  96
       if(t == HTML.Tag.SPAN) {
 362  0
         TagAction action = (TagAction) ca;
 363  0
         if (action != null) {
 364   
           /**
 365   
            * remember which part we're in for handleSimpleTag
 366   
            */
 367  0
           inSpan = false;
 368   
 
 369  0
           action.end(t);
 370   
         }
 371   
       }
 372   
       else {
 373  96
         super.handleEndTag(t, pos);
 374   
       }
 375   
     }
 376   
 
 377   
     /**
 378   
      * this action is used to read the style attribute from
 379   
      * a SPAN tag and to map from HTML to Java attributes.
 380   
      */
 381   
     class SHTMLCharacterAction extends HTMLDocument.HTMLReader.CharacterAction {
 382  0
       public void start(HTML.Tag t, MutableAttributeSet attr) {
 383  0
         pushCharacterStyle();
 384  0
         if (attr.isDefined(IMPLIED)) {
 385  0
           attr.removeAttribute(IMPLIED);
 386   
         }
 387  0
         charAttr.addAttribute(t, attr.copyAttributes());
 388  0
         if (styleAttributes != null) {
 389  0
           charAttr.addAttributes(styleAttributes);
 390   
         }
 391  0
         if(charAttr.isDefined(HTML.Tag.SPAN)) {
 392  0
           charAttr.removeAttribute(HTML.Tag.SPAN);
 393   
         }
 394   
         //System.out.println("mapping attributes");
 395  0
         charAttr = (MutableAttributeSet) new AttributeMapper(charAttr).
 396   
                    getMappedAttributes(AttributeMapper.toJava);
 397   
       }
 398   
 
 399  0
       public void end(HTML.Tag t) {
 400  0
         popCharacterStyle();
 401   
       }
 402   
     }
 403   
   }
 404   
 
 405   
   /* -------- custom reader implementation end -------- */
 406   
 
 407   
 }