Clover coverage report -
Coverage timestamp: Sun Apr 18 2004 21:32:30 EDT
file stats: LOC: 353   Methods: 12
NCLOC: 163   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
CombinedAttribute.java 71.9% 64.6% 83.3% 67.8%
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.AttributeSet;
 21   
 import javax.swing.text.html.CSS;
 22   
 import java.util.Enumeration;
 23   
 import java.util.Vector;
 24   
 import javax.swing.text.Element;
 25   
 import javax.swing.text.Style;
 26   
 
 27   
 /**
 28   
  * A class to represent an attribute combining several other attributes.
 29   
  *
 30   
  * <p>The <a href="http://www.w3.org/TR/REC-CSS1">CSS 1.0 specification</a>
 31   
  * defines 'shorthand properties' which can hold more than
 32   
  * one value separated by blanks. Depending on the number of values inside
 33   
  * the property the values are applied following a fixed ratio.</p>
 34   
  *
 35   
  * <p>Following is an excerpt of the spec for CSS property
 36   
  * <code>border-width</code></p>
 37   
  * <pre>
 38   
  * There can be from one to four values, with the following interpretation:
 39   
  *     one value: all four border widths are set to that value
 40   
  *     two values: top and bottom border widths are set to the
 41   
  *                    first value, right and left are set to the second
 42   
  *     three values: top is set to the first, right and left are set to
 43   
  *                    the second, bottom is set to the third
 44   
  *     four values: top, right, bottom and left, respectively
 45   
  * </pre>
 46   
  *
 47   
  * <p>In SimplyHTML this spec is used on properties margin,
 48   
  * padding, border-width and border-color</p>
 49   
  *
 50   
  * @author Ulrich Hilger
 51   
  * @author Light Development
 52   
  * @author <a href="http://www.lightdev.com">http://www.lightdev.com</a>
 53   
  * @author <a href="mailto:info@lightdev.com">info@lightdev.com</a>
 54   
  * @author published under the terms and conditions of the
 55   
  *      GNU General Public License,
 56   
  *      for details see file gpl.txt in the distribution
 57   
  *      package of this software
 58   
  *
 59   
  * @version stage 11, April 27, 2003
 60   
  */
 61   
 
 62   
 public class CombinedAttribute {
 63   
 
 64   
   /** index of top value */
 65   
   public static final int ATTR_TOP = 0;
 66   
 
 67   
   /** index of right value */
 68   
   public static final int ATTR_RIGHT = 1;
 69   
 
 70   
   /** index of bottom value */
 71   
   public static final int ATTR_BOTTOM = 2;
 72   
 
 73   
   /** index of left value */
 74   
   public static final int ATTR_LEFT = 3;
 75   
 
 76   
   /** the values of this <code>CombinedAttribute</code> */
 77   
   private String[] values = new String[4];
 78   
 
 79   
   /**
 80   
    * the attribute key the values of this
 81   
    * <code>CombinedAttribute</code> belong to
 82   
    */
 83   
   private Object attributeKey;
 84   
 
 85   
   /** indicates which sides were present in the attribute set */
 86   
   private boolean[] present = new boolean[4];
 87   
 
 88   
   /** table with attribute names from the source attribute set */
 89   
   private Vector aNames = new Vector();
 90   
 
 91   
   /** indicates if attributes of parent elements shall be used */
 92   
   private boolean includeParents;
 93   
 
 94   
   /**
 95   
    * construct a <code>CombinedAttribute</code> for a certain
 96   
    * attribute out of a given set of attributes
 97   
    *
 98   
    * @param key  the attribute key to get single attribute values from
 99   
    * @param a  the set of attributes to get the attribute of type 'key'
 100   
    */
 101  86
   public CombinedAttribute(Object key, AttributeSet a, boolean includeParents) {
 102  86
     attributeKey = key;
 103  86
     this.includeParents = includeParents;
 104   
 
 105   
     // get names in this attribute set to filter out parent attributes later
 106  86
     Enumeration names = a.getAttributeNames();
 107  86
     while(names.hasMoreElements()) {
 108  528
       aNames.addElement(names.nextElement());
 109   
     }
 110   
 
 111   
     // now load attributes into this object
 112  86
     Object attr = a.getAttribute(key);
 113  86
     if(attr != null) {
 114   
       //System.out.println("  construct CombinedAttribute attr=" + attr);
 115  1
       copyValues(Util.tokenize(attr.toString(), " "));
 116   
     }
 117   
     else {
 118  85
       copyValues(key, a);
 119   
     }
 120   
   }
 121   
 
 122   
   /**
 123   
    * copy the values for individual border settings from a given
 124   
    * set of attributes into the structure top, right, bottom, left of
 125   
    * this <code>CombinedAttribute</code>
 126   
    *
 127   
    * <p>Used in cases where attributes are not found for a 'shorthand
 128   
    * property' such as PADDING or MARGIN.</p>
 129   
    *
 130   
    * @param key  the 'shorthand property' to copy individual attributes for
 131   
    * @param a  the set of attributes to copy from
 132   
    */
 133  85
   private void copyValues(Object key, AttributeSet a) {
 134  85
     if(key.equals(CSS.Attribute.BORDER_WIDTH)) {
 135  27
       setValue(ATTR_TOP, CSS.Attribute.BORDER_TOP_WIDTH, a);
 136  27
       setValue(ATTR_RIGHT, CSS.Attribute.BORDER_RIGHT_WIDTH, a);
 137  27
       setValue(ATTR_BOTTOM, CSS.Attribute.BORDER_BOTTOM_WIDTH, a);
 138  27
       setValue(ATTR_LEFT, CSS.Attribute.BORDER_LEFT_WIDTH, a);
 139   
     }
 140  58
     else if(key.equals(CSS.Attribute.PADDING)) {
 141  29
       setValue(ATTR_TOP, CSS.Attribute.PADDING_TOP, a);
 142  29
       setValue(ATTR_RIGHT, CSS.Attribute.PADDING_RIGHT, a);
 143  29
       setValue(ATTR_BOTTOM, CSS.Attribute.PADDING_BOTTOM, a);
 144  29
       setValue(ATTR_LEFT, CSS.Attribute.PADDING_LEFT, a);
 145   
     }
 146  29
     else if(key.equals(CSS.Attribute.MARGIN)) {
 147  29
       setValue(ATTR_TOP, CSS.Attribute.MARGIN_TOP, a);
 148  29
       setValue(ATTR_RIGHT, CSS.Attribute.MARGIN_RIGHT, a);
 149  29
       setValue(ATTR_BOTTOM, CSS.Attribute.MARGIN_BOTTOM, a);
 150  29
       setValue(ATTR_LEFT, CSS.Attribute.MARGIN_LEFT, a);
 151   
     }
 152   
   }
 153   
 
 154   
   /**
 155   
    * set the value of a certain side from a given attribute key and
 156   
    * set of attributes.
 157   
    *
 158   
    * @param side  the side to set the value for, one of ATTR_TOP,
 159   
    *    ATTR_RIGHT, ATTR_BOTTOM and ATTR_LEFT
 160   
    * @param key  the attribute key to get the value from
 161   
    * @param a  the set of attributes to get the value from
 162   
    */
 163  340
   private void setValue(int side, Object key, AttributeSet a) {
 164  340
     if((includeParents) || ((!includeParents) && (aNames.contains(key)))) { // filter out parent attributes
 165  116
       Object attr = a.getAttribute(key);
 166  116
       if(attr != null) {
 167  102
         values[side] = attr.toString();
 168  102
         present[side] = true;
 169   
       }
 170   
       else {
 171  14
         values[side] = defaultValue(attributeKey);
 172  14
         present[side] = true;
 173   
       }
 174   
     }
 175   
     else { // key not present, set default value
 176  224
       values[side] = defaultValue(attributeKey);
 177  224
       present[side] = false;
 178   
     }
 179   
   }
 180   
 
 181   
   /**
 182   
    * determine whether or not the set of attributes this
 183   
    * <code>CombinedAttribute</code> was created from contained any
 184   
    * of the attributes in this <code>CombinedAttribute</code>
 185   
    *
 186   
    * <p>Can be used for instance to determine whether or not this
 187   
    * <code>CombinedAttribute</code> should be written</p>
 188   
    *
 189   
    * @return true, if this <code>CombinedAttribute</code> contains
 190   
    * default values only, false if not
 191   
    */
 192  78
   public boolean isEmpty() {
 193  78
     boolean notEmpty = false;
 194  78
     int i = 0;
 195  78
     while(!notEmpty && i < present.length) {
 196  246
       notEmpty = present[i];
 197  246
       i++;
 198   
     }
 199  78
     return !notEmpty;
 200   
   }
 201   
 
 202   
   /**
 203   
    * get the default value for a given key
 204   
    *
 205   
    * @param key  the attribute key to get the default value for
 206   
    *
 207   
    * @return the default value for the given key
 208   
    */
 209  238
   private String defaultValue(Object key) {
 210  238
     String value = "0";
 211  238
     if(key.equals(CSS.Attribute.BORDER_COLOR)) {
 212  0
       value = "#000000";
 213   
     }
 214  238
     return value;
 215   
   }
 216   
 
 217   
   /**
 218   
    * get the side opposite of a given side
 219   
    *
 220   
    * @param side  the side to get the opposite of
 221   
    *
 222   
    * @return the opposite side of the given side
 223   
    */
 224  0
   public int getOppositeSide(int side) {
 225  0
     int oppositeSide = -1;
 226  0
     switch(side) {
 227  0
       case ATTR_TOP:
 228  0
         oppositeSide = ATTR_BOTTOM;
 229  0
         break;
 230  0
       case ATTR_RIGHT:
 231  0
         oppositeSide = ATTR_LEFT;
 232  0
         break;
 233  0
       case ATTR_BOTTOM:
 234  0
         oppositeSide = ATTR_TOP;
 235  0
         break;
 236  0
       case ATTR_LEFT:
 237  0
         oppositeSide = ATTR_RIGHT;
 238  0
         break;
 239   
     }
 240  0
     return oppositeSide;
 241   
   }
 242   
 
 243   
   /**
 244   
    * copy the atribute value(s) found in a 'shorthand property' such
 245   
    * as PADDING or MARGIN into the structure top, right, bottom, left of
 246   
    * this <code>CombinedAttribute</code>
 247   
    *
 248   
    * @param v  the array of Strings holding the found values
 249   
    */
 250  1
   private void copyValues(String[] v) {
 251  1
     switch(v.length) {
 252  1
       case 1:
 253  1
         for(int i = 0; i < values.length; i++) {
 254  4
           values[i] = v[0];
 255   
         }
 256  1
         break;
 257  0
       case 2:
 258  0
         values[ATTR_TOP] = v[ATTR_TOP];
 259  0
         values[ATTR_RIGHT] = v[ATTR_RIGHT];
 260  0
         values[ATTR_BOTTOM] = v[ATTR_TOP];
 261  0
         values[ATTR_LEFT] = v[ATTR_RIGHT];
 262  0
         break;
 263  0
       case 3:
 264  0
         values[ATTR_TOP] = v[ATTR_TOP];
 265  0
         values[ATTR_RIGHT] = v[ATTR_RIGHT];
 266  0
         values[ATTR_BOTTOM] = v[ATTR_BOTTOM];
 267  0
         values[ATTR_LEFT] = v[ATTR_RIGHT];
 268  0
         break;
 269  0
       case 4:
 270  0
         for(int i = 0; i < values.length; i++) {
 271  0
           values[i] = v[i];
 272   
         }
 273  0
         break;
 274   
     }
 275   
   }
 276   
 
 277   
   /**
 278   
    * set one attribute of this <code>CombinedAttribute</code>
 279   
    *
 280   
    * @param side  the side to set the attribute for, one of ATTR_TOP,
 281   
    *   ATTR_RIGHT, ATTR_BOTTOM, ATTR_LEFT
 282   
    * @param value  the attribute value to set
 283   
    */
 284  8
   public void setAttribute(int side, String value) {
 285  8
     values[side] = value;
 286   
   }
 287   
 
 288   
   /**
 289   
    * get one attribute of this <code>CombinedAttribute</code>
 290   
    *
 291   
    * @param side  the side to get the attribute for, one of ATTR_TOP,
 292   
    *   ATTR_RIGHT, ATTR_BOTTOM, ATTR_LEFT
 293   
    *
 294   
    * @return  the attribute value for the specified side or null, if the
 295   
    *    attribute key provided in the constructor was not found
 296   
    */
 297  33
   public String getAttribute(int side) {
 298  33
     return values[side];
 299   
   }
 300   
 
 301   
   /**
 302   
    * get the attribute key this <code>CombinedAttribute</code> represents
 303   
    *
 304   
    * @return the attribute key
 305   
    */
 306  0
   public Object getAttributeKey() {
 307  0
     return attributeKey;
 308   
   }
 309   
 
 310   
   /**
 311   
    * get all values of this <code>CombinedAttribute</code>
 312   
    * as one attribute.
 313   
    *
 314   
    * @return a String having all values delimited by blanks
 315   
    *     in the order top right, bottom, left or null if no
 316   
    *     attributes were found
 317   
    */
 318  35
   public String getAttribute() {
 319  35
     String result = null;
 320  35
     StringBuffer buf = new StringBuffer();
 321  35
     if(values[0] != null) {
 322  35
       buf.append(values[0]);
 323  35
       int additionalValueCount = 3;
 324  35
       if(values[ATTR_RIGHT].equalsIgnoreCase(values[ATTR_LEFT])) {
 325  35
         --additionalValueCount; // total 3
 326  35
         if(values[ATTR_TOP].equalsIgnoreCase(values[ATTR_BOTTOM])) {
 327  35
           --additionalValueCount; // total 2
 328  35
           if(values[ATTR_TOP].equalsIgnoreCase(values[ATTR_RIGHT])) {
 329  35
             --additionalValueCount; // total 1
 330   
           }
 331   
         }
 332   
       }
 333  35
       appendValues(buf, additionalValueCount);
 334  35
       result = buf.toString();
 335   
     }
 336  35
     return result;
 337   
   }
 338   
 
 339   
   /**
 340   
    * append a given number of values to a given output buffer
 341   
    * starting with ATTR_RIGHT and necessarily continuing
 342   
    * with ATTR_BOTTOM and ATTR_LEFT ( helper method to getAttribute() )
 343   
    *
 344   
    * @param buf  the output buffer to append to
 345   
    * @param count  the number of values to append
 346   
    */
 347  35
   private void appendValues(StringBuffer buf, int count) {
 348  35
     for(int i = 1; i < count + 1; i++) {
 349  0
       buf.append(' ');
 350  0
       buf.append(values[i]);
 351   
     }
 352   
   }
 353   
 }