001    package edu.nrao.sss.html;
002    
003    import javax.swing.text.html.HTML;
004    
005    /**
006     * An attribute (see {@link javax.swing.text.html.HTML.Attribute})
007     * of an HTML element.
008     * <p>
009     * <b>Version Info:</b>
010     * <table style="margin-left:2em">
011     *   <tr><td>$Revision: 545 $</td></tr>
012     *   <tr><td>$Date: 2007-04-19 10:38:45 -0600 (Thu, 19 Apr 2007) $</td></tr>
013     *   <tr><td>$Author: dharland $</td></tr>
014     * </table></p>
015     * 
016     * @author David M. Harland
017     * @since 2007-03-15
018     */
019    public class HtmlAttribute
020    {
021      private HTML.Attribute type;  //Not mutable after construction
022      private String         value;
023      
024      /**
025       * Creates a new HTML attribute of the given type and value.
026       * 
027       * @param attrName the name, or type, of this attribute.
028       *                 This value must be a valid attribute name.
029       *                 Validity is determined by
030       *                 {@link HTML#getAttributeKey(String)}.
031       *                 A list of known attribute names may be found in
032       *                 {@link javax.swing.text.html.HTML.Attribute}.</p>  
033       *                 
034       * @param attrValue the value of this attribute.
035       *                  See {@link #setValue(String)} for the rules about this
036       *                  parameter.
037       */
038      public HtmlAttribute(String attrName, String attrValue)
039      {
040        //Use name to get a valid attribute
041        type = HTML.getAttributeKey(attrName);
042        
043        if (type == null)
044          throw new IllegalArgumentException("Attribute name [" + attrName +
045                                             "] is not recognized as a valid name.");
046        setValue(attrValue);
047      }
048      
049      /**
050       * Creates a new HTML attribute of the given type and value.
051       * 
052       * @param attrType the type of this attribute.  If this parameter is
053       *                 <i>null</i> an {@code IllegalArgumentException}
054       *                 will be thrown.
055       *                 
056       * @param attrValue the value of this attribute.
057       *                  See {@link #setValue(String)} for the rules about this
058       *                  parameter.
059       */
060      public HtmlAttribute(HTML.Attribute attrType, String attrValue)
061      {
062        if (attrType == null)
063          throw new IllegalArgumentException("May not have null attribute type.");
064        
065        type = attrType;
066        setValue(attrValue);
067      }
068      
069      /**
070       * Creates a new HTML attribute of the given type and with a value of
071       * the empty string (<tt>""</tt>).
072       * 
073       * @param attrType the type of this attribute.  If this parameter is
074       *                 <i>null</i> an {@code IllegalArgumentException}
075       *                 will be thrown.
076       */
077      public HtmlAttribute(HTML.Attribute attrType)
078      {
079        this(attrType, "");
080      }
081      
082      /**
083       * Returns the type of this attribute.
084       * @return the type of this attribute.  This value is guaranteeed to be
085       *         non-null.
086       */
087      public HTML.Attribute getType()
088      {
089        return type;
090      }
091      
092      /**
093       * Sets the value of this attribute.
094       * 
095       * @param newValue a new value for this attribute.  If this parameter is
096       *                 <i>null</i>, it will be replaced by the empty
097       *                 string (<tt>""</tt>).  This parameter may contain
098       *                 single quote (<tt>'</tt>) or double quote
099       *                 (<tt>"</tt>) characters, but not both.  If it does,
100       *                 an {@code IllegalArgumentException} will be thrown
101       *                 and this attribute's value will be left in its
102       *                 present state.
103       */
104      public final void setValue(String newValue)
105      {
106        if (newValue == null)
107          newValue = "";
108        
109        if (newValue.contains("'") && newValue.contains("\""))
110          throw new IllegalArgumentException(
111            "newValue [" + newValue +
112            "] may not have both single and double quotes.");
113        
114        value = newValue;
115      }
116      
117      /**
118       * Returns the value of this attribute.
119       * 
120       * @return the value of this attribute.  This value is guaranteeed to be
121       *         non-null, but could be an empty string (<tt>""</tt>).
122       */
123      public String getValue()
124      {
125        return value;
126      }
127      
128      /**
129       * Creates and returns a new HTML attribute by parsing {@code nameValueText}.
130       * 
131       * @param nameValueText the HTML form of this attribute.
132       * The expected form of the text is:
133       * <pre>  name="value"</pre>
134       * Values may be quoted with either single (') or double (") quotes.
135       * Leading and trailing whitespace is permitted and will be trimmed during
136       * parsing.
137       * The <tt>name</tt> must be a valid attribute name.  Validity is determined
138       * by {@link HTML#getAttributeKey(String)}.  A list of known attribute names
139       * may be found in {@link javax.swing.text.html.HTML.Attribute}.</p>  
140       *  
141       * @return a new HTML attribute by parsing {@code nameValueText}.
142       * 
143       * @throws IllegalArgumentException if anything goes wrong during parsing.
144       */
145      public static HtmlAttribute parse(String nameValueText)
146      {
147        StringBuilder nameValueBuff = new StringBuilder(nameValueText.trim());
148        StringBuilder tempBuff      = new StringBuilder();
149        int           charCount     = nameValueBuff.length();
150        
151        String name, value;
152    
153        //Ensure we have format name="value"
154        try
155        {
156          //Look for the name
157          int  c  = 0;
158          char ch = nameValueBuff.charAt(c);
159          while (ch != '=')
160          {
161            tempBuff.append(ch);
162            ch = nameValueBuff.charAt(++c);
163          }
164    
165          name = tempBuff.toString().trim().toLowerCase();
166          
167          //Skip any white space after '='
168          c++;
169          while (Character.isWhitespace(nameValueBuff.charAt(c)))
170            c++;
171          
172          //Look for the value
173          tempBuff.delete(0, tempBuff.length());
174          char quoteChar = nameValueBuff.charAt(c); //Could be " or '
175          ch = nameValueBuff.charAt(++c);
176          while (ch != quoteChar)
177          {
178            tempBuff.append(ch);
179            ch = nameValueBuff.charAt(++c);
180          }
181          
182          if (++c < charCount)
183            throw new IllegalArgumentException(
184              "Extra characters found beyond value's closing quote.");
185    
186          value = tempBuff.toString();
187        }
188        catch (Exception ex)
189        {
190          throw new IllegalArgumentException("Could not parse nameValueText [" +
191                                             nameValueText + "]: " +
192                                             ex.getMessage());
193        }
194    
195        //Use name to get a valid attribute
196        HTML.Attribute attrType = HTML.getAttributeKey(name);
197        
198        if (attrType == null)
199          throw new IllegalArgumentException("Attribute name [" + name +
200                                             "] in nameValueText [" + nameValueText+
201                                             "] is not recognized as a valid name.");
202        
203        return new HtmlAttribute(attrType, value);
204      }
205      
206      /**
207       * Returns a string of the form <tt>name="value"</tt>.
208       * If <i>value</i> itself contains double-quote characters,
209       * the returned string will be of the form <tt>name='value'</tt>.
210       */
211      @Override
212      public String toString()
213      {
214        char quoteChar = value.contains("\"") ? '\'' : '"';
215        
216        StringBuilder buff = new StringBuilder(type.toString());
217        
218        buff.append('=').append(quoteChar).append(value).append(quoteChar);
219        
220        return buff.toString();
221      }
222      
223      /** Returns <i>true</i> if {@code o} is equal to this attribute. */
224      @Override
225      public boolean equals(Object o)
226      {
227        //Quick exit if o is this
228        if (o == this)
229          return true;
230        
231        //Quick exit if o is null
232        if (o == null)
233          return false;
234        
235        //Quick exit if classes are different
236        if (!o.getClass().equals(this.getClass()))
237          return false;
238       
239        HtmlAttribute other = (HtmlAttribute)o;
240    
241        return this.type.equals(other.type) &&
242               this.value.equals(other.value);
243      }
244      
245      @Override
246      /** Returns a hash code value for this cell. */
247      public int hashCode()
248      {
249        //Taken from the Effective Java book by Joshua Bloch.
250        //The constants 17 & 37 are arbitrary & carry no meaning.
251        int result = 17;
252        
253        //You MUST keep this method in synch w/ equals
254        
255        result = 37 * result + type.hashCode();
256        result = 37 * result + value.hashCode();
257        
258        return result;
259      }
260      /*
261      public static void main(String[] args)
262      {
263        HtmlAttribute attr = HtmlAttribute.parse(" name  ='Richard \"Dick\" Cheney'  ");
264        System.out.println(attr);
265        
266        attr = HtmlAttribute.parse(attr.toString());
267        System.out.println(attr);
268        
269        attr = HtmlAttribute.parse("name='Barney' class=\"72\" color='pale'");
270        System.out.println(attr);
271        
272        //attr = HtmlAttribute.parse("name='Fred Flintstone\"");
273        //System.out.println(attr);
274        
275        //attr = HtmlAttribute.parse("fruit=\"Apple\"");
276        //System.out.println(attr);
277      }
278      */
279    }