KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > au > id > jericho > lib > html > Attributes


1 // Jericho HTML Parser - Java based library for analysing and manipulating HTML
2
// Version 2.2
3
// Copyright (C) 2006 Martin Jericho
4
// http://sourceforge.net/projects/jerichohtml/
5
//
6
// This library is free software; you can redistribute it and/or
7
// modify it under the terms of the GNU Lesser General Public
8
// License as published by the Free Software Foundation; either
9
// version 2.1 of the License, or (at your option) any later version.
10
// http://www.gnu.org/copyleft/lesser.html
11
//
12
// This library is distributed in the hope that it will be useful,
13
// but WITHOUT ANY WARRANTY; without even the implied warranty of
14
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
// Lesser General Public License for more details.
16
//
17
// You should have received a copy of the GNU Lesser General Public
18
// License along with this library; if not, write to the Free Software
19
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20

21 package au.id.jericho.lib.html;
22
23 import au.id.jericho.lib.html.nodoc.*;
24 import java.util.*;
25 import java.io.*;
26
27 /**
28  * Represents the list of {@link Attribute} objects present within a particular {@link StartTag}.
29  * <p>
30  * This segment starts at the end of the start tag's {@linkplain StartTag#getName() name}
31  * and ends at the end of the last attribute.
32  * <p>
33  * The attributes in this list are a representation of those found in the source document and are not modifiable.
34  * The {@link AttributesOutputSegment} class provides the means to add, delete or modify attributes and
35  * their values for inclusion in an {@link OutputDocument}.
36  * <p>
37  * If too many syntax errors are encountered while parsing a start tag's attributes, the parser rejects the entire start tag
38  * and generates a {@linkplain Source#setLogWriter(Writer) log} entry.
39  * The threshold for the number of errors allowed can be set using the {@link #setDefaultMaxErrorCount(int)} static method.
40  * <p>
41  * Obtained using the {@link StartTag#getAttributes()} method, or explicitly using the {@link Source#parseAttributes(int pos, int maxEnd)} method.
42  * <p>
43  * It is common for instances of this class to contain no attributes.
44  * <p>
45  * See also the XML 1.0 specification for <a target="_blank" HREF="http://www.w3.org/TR/REC-xml#dt-attr">attributes</a>.
46  * <p>
47  * Note that before version 2.0 the segment ended just before the tag's
48  * {@linkplain StartTagType#getClosingDelimiter() closing delimiter} instead of at the end of the last attribute.
49  *
50  * @see StartTag
51  * @see Attribute
52  */

53 public final class Attributes extends SequentialListSegment {
54     private final LinkedList attributeList; // never null
55

56     // parsing states:
57
private static final int AFTER_TAG_NAME=0;
58     private static final int BETWEEN_ATTRIBUTES=1;
59     private static final int IN_NAME=2;
60     private static final int AFTER_NAME=3; // this only happens if an attribute name is followed by whitespace
61
private static final int START_VALUE=4;
62     private static final int IN_VALUE=5;
63     private static final int AFTER_VALUE_FINAL_QUOTE=6;
64
65     private static int defaultMaxErrorCount=2; // defines maximum number of minor errors that can be encountered in attributes before entire start tag is rejected.
66

67     private Attributes(final Source source, final int begin, final int end, final LinkedList attributeList) {
68         super(source,begin,end);
69         this.attributeList=attributeList;
70     }
71
72     /** called from StartTagType.parseAttributes(Source, int startTagBegin, String tagName) */
73     static Attributes construct(final Source source, final int startTagBegin, final StartTagType startTagType, final String JavaDoc tagName) {
74         return construct(source,"StartTag",AFTER_TAG_NAME,startTagBegin,-1,-1,startTagType,tagName,defaultMaxErrorCount);
75     }
76
77     /** called from StartTag.parseAttributes(int maxErrorCount) */
78     static Attributes construct(final Source source, final int startTagBegin, final int attributesBegin, final int maxEnd, final StartTagType startTagType, final String JavaDoc tagName, final int maxErrorCount) {
79         return construct(source,"Attributes for StartTag",BETWEEN_ATTRIBUTES,startTagBegin,attributesBegin,maxEnd,startTagType,tagName,maxErrorCount);
80     }
81
82     /** called from Source.parseAttributes(int pos, int maxEnd, int maxErrorCount) */
83     static Attributes construct(final Source source, final int begin, final int maxEnd, final int maxErrorCount) {
84         return construct(source,"Attributes",BETWEEN_ATTRIBUTES,begin,-1,maxEnd,StartTagType.NORMAL,null,maxErrorCount);
85     }
86
87     /**
88      * Any &lt; character found within the start tag is treated as though it is part of the attribute
89      * list, which is consistent with the way IE treats it.
90      * In some cases an invalid character results in the entire start tag being rejected.
91      * This may seem strict, but we have to be able to distinguish whether any
92      * particular < found in the source is actually the start of a tag or not.
93      * Being too lenient with attributes means more chance of false positives, which in turn
94      * means surrounding tags may be ignored.
95      * @param logBegin the position of the beginning of the object being searched (for logging)
96      * @param attributesBegin the position of the beginning of the attribute list, or -1 if it should be calculated automatically from logBegin.
97      * @param maxEnd the position at which the attributes must end if a terminating character is not found, or -1 if no maximum.
98      * @param tagName the name of the enclosing StartTag, or null if constucting attributes directly.
99      */

100     private static Attributes construct(final Source source, final String JavaDoc logType, int state, final int logBegin, int attributesBegin, final int maxEnd, final StartTagType startTagType, final String JavaDoc tagName, final int maxErrorCount) {
101         boolean isClosingSlashIgnored=false;
102         if (tagName!=null) {
103             // 'logBegin' parameter is the start of the associated start tag
104
if (attributesBegin==-1) attributesBegin=logBegin+1+tagName.length();
105             if (startTagType==StartTagType.NORMAL && HTMLElements.isClosingSlashIgnored(tagName)) isClosingSlashIgnored=true;
106         } else {
107             attributesBegin=logBegin;
108         }
109         int attributesEnd=attributesBegin;
110         final LinkedList attributeList=new LinkedList();
111         final ParseText parseText=source.getParseText();
112         int i=attributesBegin;
113         char quote=' ';
114         Segment nameSegment=null;
115         String JavaDoc key=null;
116         int currentBegin=-1;
117         boolean isTerminatingCharacter=false;
118         int errorCount=0;
119         try {
120             while (!isTerminatingCharacter) {
121                 if (i==maxEnd || startTagType.atEndOfAttributes(source,i,isClosingSlashIgnored)) isTerminatingCharacter=true;
122                 final char ch=parseText.charAt(i);
123                 switch (state) {
124                     case IN_VALUE:
125                         if (isTerminatingCharacter || ch==quote || (quote==' ' && isWhiteSpace(ch))) {
126                             Segment valueSegment;
127                             Segment valueSegmentIncludingQuotes;
128                             if (quote==' ') {
129                                 valueSegment=valueSegmentIncludingQuotes=new Segment(source,currentBegin,i);
130                             } else {
131                                 if (isTerminatingCharacter) {
132                                     if (i==maxEnd) {
133                                         if (source.isLoggingEnabled()) log(source,logType,tagName,logBegin,"terminated in the middle of a quoted attribute value",i);
134                                         if (reachedMaxErrorCount(++errorCount,source,logType,tagName,logBegin,maxErrorCount)) return null;
135                                         valueSegment=new Segment(source,currentBegin,i);
136                                         valueSegmentIncludingQuotes=new Segment(source,currentBegin-1,i); // this is missing the end quote
137
} else {
138                                         // don't want to terminate, only encountered a terminating character in the middle of a quoted value
139
isTerminatingCharacter=false;
140                                         break;
141                                     }
142                                 } else {
143                                     valueSegment=new Segment(source,currentBegin,i);
144                                     valueSegmentIncludingQuotes=new Segment(source,currentBegin-1,i+1);
145                                 }
146                             }
147                             attributeList.add(new Attribute(source, key, nameSegment, valueSegment, valueSegmentIncludingQuotes));
148                             attributesEnd=valueSegmentIncludingQuotes.getEnd();
149                             state=BETWEEN_ATTRIBUTES;
150                         } else if (ch=='<' && quote==' ') {
151                             if (source.isLoggingEnabled()) log(source,logType,tagName,logBegin,"rejected because of '<' character in unquoted attribute value",i);
152                             return null;
153                         }
154                         break;
155                     case IN_NAME:
156                         if (isTerminatingCharacter || ch=='=' || isWhiteSpace(ch)) {
157                             nameSegment=new Segment(source,currentBegin,i);
158                             key=nameSegment.toString().toLowerCase();
159                             if (isTerminatingCharacter) {
160                                 attributeList.add(new Attribute(source,key,nameSegment)); // attribute with no value
161
attributesEnd=i;
162                             } else {
163                                 state=(ch=='=' ? START_VALUE : AFTER_NAME);
164                             }
165                         } else if (!Tag.isXMLNameChar(ch)) {
166                             // invalid character detected in attribute name.
167
// only reject whole start tag if it is a < character or if the error count is exceeded.
168
if (ch=='<') {
169                                 if (source.isLoggingEnabled()) log(source,logType,tagName,logBegin,"rejected because of '<' character in attribute name",i);
170                                 return null;
171                             }
172                             if (isInvalidEmptyElementTag(startTagType,source,i,logType,tagName,logBegin)) break;
173                             if (source.isLoggingEnabled()) log(source,logType,tagName,logBegin,"contains attribute name with invalid character",i);
174                             if (reachedMaxErrorCount(++errorCount,source,logType,tagName,logBegin,maxErrorCount)) return null;
175                         }
176                         break;
177                     case AFTER_NAME:
178                         // attribute name has been followed by whitespace, but may still be followed by an '=' character.
179
if (isTerminatingCharacter || !(ch=='=' || isWhiteSpace(ch))) {
180                             attributeList.add(new Attribute(source,key,nameSegment)); // attribute with no value
181
attributesEnd=nameSegment.getEnd();
182                             if (isTerminatingCharacter) break;
183                             // The current character is the first character of an attribute name
184
state=BETWEEN_ATTRIBUTES;
185                             i--; // want to reparse the same character again, so decrement i. Note we could instead just fall into the next case statement without a break, but such code is always discouraged.
186
} else if (ch=='=') {
187                             state=START_VALUE;
188                         }
189                         break;
190                     case BETWEEN_ATTRIBUTES:
191                         if (!isTerminatingCharacter) {
192                             // the quote variable is used here to make sure whitespace has come after the last quoted attribute value
193
if (isWhiteSpace(ch)) {
194                                 quote=' ';
195                             } else {
196                                 if (quote!=' ') {
197                                     if (source.isLoggingEnabled()) log(source,logType,tagName,logBegin,"has missing whitespace after quoted attribute value",i);
198                                     // log this as an error but don't count it
199
}
200                                 if (!Tag.isXMLNameStartChar(ch)) {
201                                     // invalid character detected as first character of attribute name.
202
// only reject whole start tag if it is a < character or if the error count is exceeded.
203
if (ch=='<') {
204                                         if (source.isLoggingEnabled()) log(source,logType,tagName,logBegin,"rejected because of '<' character",i);
205                                         return null;
206                                     }
207                                     if (isInvalidEmptyElementTag(startTagType,source,i,logType,tagName,logBegin)) break;
208                                     if (startTagType==StartTagType.NORMAL && startTagType.atEndOfAttributes(source,i,false)) {
209                                         // This checks whether we've found the characters "/>" but it wasn't recognised as the closing delimiter because isClosingSlashIgnored is true.
210
if (source.isLoggingEnabled()) log(source,logType,tagName,logBegin,"contains a '/' character before the closing '>', which is ignored because tags of this name cannot be empty-element tags");
211                                         break;
212                                     }
213                                     if (source.isLoggingEnabled()) log(source,logType,tagName,logBegin,"contains attribute name with invalid first character",i);
214                                     if (reachedMaxErrorCount(++errorCount,source,logType,tagName,logBegin,maxErrorCount)) return null;
215                                 }
216                                 state=IN_NAME;
217                                 currentBegin=i;
218                             }
219                         }
220                         break;
221                     case START_VALUE:
222                         currentBegin=i;
223                         if (isTerminatingCharacter) {
224                             if (source.isLoggingEnabled()) log(source,logType,tagName,logBegin,"has missing attribute value after '=' sign",i);
225                             // log this as an error but don't count it
226
final Segment valueSegment=new Segment(source,i,i);
227                             attributeList.add(new Attribute(source,key,nameSegment,valueSegment,valueSegment));
228                             attributesEnd=i;
229                             state=BETWEEN_ATTRIBUTES;
230                             break;
231                         }
232                         if (isWhiteSpace(ch)) break; // just ignore whitespace after the '=' sign as nearly all browsers do.
233
if (ch=='<') {
234                             if (source.isLoggingEnabled()) log(source,logType,tagName,logBegin,"rejected because of '<' character at start of attribuite value",i);
235                             return null;
236                         } else if (ch=='\'' || ch=='"') {
237                             quote=ch;
238                             currentBegin++;
239                         } else {
240                             quote=' ';
241                         }
242                         state=IN_VALUE;
243                         break;
244                     case AFTER_TAG_NAME:
245                         if (!isTerminatingCharacter) {
246                             if (!isWhiteSpace(ch)) {
247                                 if (isInvalidEmptyElementTag(startTagType,source,i,logType,tagName,logBegin)) break;
248                                 if (source.isLoggingEnabled()) log(source,logType,tagName,logBegin,"rejected because name contains invalid character",i);
249                                 return null;
250                             }
251                             state=BETWEEN_ATTRIBUTES;
252                         }
253                         break;
254                 }
255                 i++;
256             }
257             return new Attributes(source,attributesBegin,attributesEnd,attributeList); // used to end at i-1
258
} catch (IndexOutOfBoundsException JavaDoc ex) {
259             if (source.isLoggingEnabled()) log(source,logType,tagName,logBegin,"rejected because it has no closing '>' character");
260             return null;
261         }
262     }
263
264     private static boolean reachedMaxErrorCount(final int errorCount, final Source source, final String JavaDoc logType, final String JavaDoc tagName, final int logBegin, final int maxErrorCount) {
265         if (errorCount<=maxErrorCount) return false;
266         if (source.isLoggingEnabled()) log(source,logType,tagName,logBegin,"rejected because it contains too many errors");
267         return true;
268     }
269
270     private static boolean isInvalidEmptyElementTag(final StartTagType startTagType, final Source source, final int i, final String JavaDoc logType, final String JavaDoc tagName, final int logBegin) {
271         // This checks whether we've found the characters "/>" but it wasn't recognised as the closing delimiter because isClosingSlashIgnored is true.
272
if (startTagType!=StartTagType.NORMAL || !startTagType.atEndOfAttributes(source,i,false)) return false;
273         if (source.isLoggingEnabled()) log(source,logType,tagName,logBegin,"contains a '/' character before the closing '>', which is ignored because tags of this name cannot be empty-element tags");
274         return true;
275     }
276
277     /**
278      * Returns the {@link Attribute} with the specified name (case insensitive).
279      * <p>
280      * If more than one attribute exists with the specified name (which is illegal HTML),
281      * the first is returned.
282      *
283      * @param name the name of the attribute to get.
284      * @return the attribute with the specified name, or <code>null</code> if no attribute with the specified name exists.
285      * @see #getValue(String name)
286      */

287     public Attribute get(final String JavaDoc name) {
288         if (size()==0) return null;
289         for (int i=0; i<size(); i++) {
290             final Attribute attribute=(Attribute)get(i);
291             if (attribute.getKey().equalsIgnoreCase(name)) return attribute;
292         }
293         return null;
294     }
295
296     /**
297      * Returns the {@linkplain CharacterReference#decode(CharSequence) decoded} value of the attribute with the specified name (case insensitive).
298      * <p>
299      * Returns <code>null</code> if no attribute with the specified name exists or
300      * the attribute {@linkplain Attribute#hasValue() has no value}.
301      * <p>
302      * This is equivalent to {@link #get(String) get(name)}<code>.</code>{@link Attribute#getValue() getValue()},
303      * except that it returns <code>null</code> if no attribute with the specified name exists instead of throwing a
304      * <code>NullPointerException</code>.
305      *
306      * @param name the name of the attribute to get.
307      * @return the {@linkplain CharacterReference#decode(CharSequence) decoded} value of the attribute with the specified name, or <code>null</code> if the attribute does not exist or {@linkplain Attribute#hasValue() has no value}.
308      * @see Attribute#getValue()
309      */

310     public String JavaDoc getValue(final String JavaDoc name) {
311         final Attribute attribute=get(name);
312         return attribute==null ? null : attribute.getValue();
313     }
314
315     /**
316      * Returns the raw (not {@linkplain CharacterReference#decode(CharSequence) decoded}) value of the attribute, or null if the attribute {@linkplain Attribute#hasValue() has no value}.
317      * <p>
318      * This is an internal convenience method.
319      *
320      * @return the raw (not {@linkplain CharacterReference#decode(CharSequence) decoded}) value of the attribute, or null if the attribute {@linkplain Attribute#hasValue() has no value}.
321      */

322     String JavaDoc getRawValue(final String JavaDoc name) {
323         final Attribute attribute=get(name);
324         return attribute==null || !attribute.hasValue() ? null : attribute.getValueSegment().toString();
325     }
326
327     /**
328      * Returns the number of attributes.
329      * <p>
330      * This is equivalent to calling the <code>size()</code> method specified in the <code>List</code> interface.
331      *
332      * @return the number of attributes.
333      */

334     public int getCount() {
335         return attributeList.size();
336     }
337
338     /**
339      * Returns an iterator over the {@link Attribute} objects in this list in order of appearance.
340      * @return an iterator over the {@link Attribute} objects in this list in order of appearance.
341      */

342     public Iterator iterator() {
343         return listIterator();
344     }
345
346     /**
347      * Returns a list iterator of the {@link Attribute} objects in this list in order of appearance,
348      * starting at the specified position in the list.
349      * <p>
350      * The specified index indicates the first item that would be returned by an initial call to the <code>next()</code> method.
351      * An initial call to the <code>previous()</code> method would return the item with the specified index minus one.
352      * <p>
353      * IMPLEMENTATION NOTE: For efficiency reasons this method does not return an immutable list iterator.
354      * Calling any of the <code>add(Object)</code>, <code>remove()</code> or <code>set(Object)</code> methods on the returned
355      * <code>ListIterator</code> does not throw an exception but could result in unexpected behaviour.
356      *
357      * @param index the index of the first item to be returned from the list iterator (by a call to the <code>next()</code> method).
358      * @return a list iterator of the items in this list (in proper sequence), starting at the specified position in the list.
359      * @throws IndexOutOfBoundsException if the specified index is out of range (<code>index &lt; 0 || index &gt; size()</code>).
360      */

361     public ListIterator listIterator(final int index) {
362         return attributeList.listIterator(index);
363     }
364
365     /**
366      * Populates the specified <code>Map</code> with the name/value pairs from these attributes.
367      * <p>
368      * Both names and values are stored as <code>String</code> objects.
369      * <p>
370      * The entries are added in order of apprearance in the source document.
371      * <p>
372      * An attribute with {@linkplain Attribute#hasValue() no value} is represented by a map entry with a <code>null</code> value.
373      * <p>
374      * Attribute values are automatically {@linkplain CharacterReference#decode(CharSequence) decoded}
375      * before storage in the map.
376      *
377      * @param attributesMap the map to populate, must not be <code>null</code>.
378      * @param convertNamesToLowerCase specifies whether all attribute names are converted to lower case in the map.
379      * @return the same map specified as the argument to the <code>attributesMap</code> parameter, populated with the name/value pairs from these attributes.
380      * @see #generateHTML(Map attributesMap)
381      */

382     public Map populateMap(final Map attributesMap, final boolean convertNamesToLowerCase) {
383         for (final Iterator i=listIterator(0); i.hasNext();) {
384             final Attribute attribute=(Attribute)i.next();
385             attributesMap.put(convertNamesToLowerCase ? attribute.getKey() : attribute.getName(),attribute.getValue());
386         }
387         return attributesMap;
388     }
389
390     /**
391      * Returns a string representation of this object useful for debugging purposes.
392      * @return a string representation of this object useful for debugging purposes.
393      */

394     public String JavaDoc getDebugInfo() {
395         final StringBuffer JavaDoc sb=new StringBuffer JavaDoc();
396         sb.append("Attributes ").append(super.getDebugInfo()).append(": ");
397         if (isEmpty()) {
398             sb.append("EMPTY");
399         } else {
400             sb.append('\n');
401             for (final Iterator i=listIterator(0); i.hasNext();) {
402                 Attribute attribute=(Attribute)i.next();
403                 sb.append(" ").append(attribute.getDebugInfo());
404             }
405         }
406         return sb.toString();
407     }
408
409     /**
410      * Returns the default maximum error count allowed when parsing attributes.
411      * <p>
412      * The system default value is 2.
413      * <p>
414      * When searching for start tags, the parser can find the end of the start tag only by
415      * {@linkplain StartTagType#parseAttributes(Source,int,String) parsing}
416      * the attributes, as it is valid HTML for attribute values to contain '&gt;' characters
417      * (see the <a target="_blank" HREF="http://www.w3.org/TR/html401/charset.html#h-5.3.2">HTML 4.01 specification section 5.3.2</a>).
418      * <p>
419      * If the source text being parsed does not follow the syntax of an attribute list at all, the parser assumes
420      * that the text which was originally identified as the beginning of of a start tag is in fact some other text,
421      * such as an invalid '&lt;' character in the middle of some text, or part of a script element.
422      * In this case the entire start tag is rejected.
423      * <p>
424      * On the other hand, it is quite common for attributes to contain minor syntactical errors,
425      * such as an invalid character in an attribute name, or a couple of special characters in
426      * {@linkplain TagType#isServerTag() server tags} that otherwise contain only attributes.
427      * For this reason the parser allows a certain number of minor errors to occur while parsing an
428      * attribute list before the entire start tag or attribute list is rejected.
429      * This property indicates the number of minor errors allowed.
430      * <p>
431      * Major syntactical errors cause the start tag or attribute list to be rejected immediately, regardless
432      * of the maximum error count setting.
433      * <p>
434      * Some errors are considered too minor to count at all (ignorable), such as missing whitespace between the end
435      * of a quoted attribute value and the start of the next attribute name.
436      * <p>
437      * The classification of particular syntax errors in attribute lists into major, minor, and ignorable is
438      * not part of the specification and may change in future versions.
439      * <p>
440      * To track errors as they occur, use the {@link Source#setLogWriter(Writer writer)} method to set the
441      * destination of the error log.
442      * <p>
443      * The value of this property is set using the {@link #setDefaultMaxErrorCount(int)} method.
444      *
445      * @return the default maximum error count allowed when parsing attributes.
446      * @see Source#parseAttributes(int pos, int maxEnd, int maxErrorCount)
447      */

448     public static int getDefaultMaxErrorCount() {
449         return defaultMaxErrorCount;
450     }
451
452     /**
453      * Sets the default maximum error count allowed when parsing attributes.
454      * <p>
455      * See the {@link #getDefaultMaxErrorCount()} method for a full description of this property.
456      *
457      * @param value the default maximum error count allowed when parsing attributes.
458      */

459     public static void setDefaultMaxErrorCount(final int value) {
460         defaultMaxErrorCount=value;
461     }
462
463     /**
464      * Returns the contents of the specified {@linkplain #populateMap(Map,boolean) attributes map} as HTML attribute name/value pairs.
465      * <p>
466      * Each attribute (including the first) is preceded by a single space, and all values are
467      * {@linkplain CharacterReference#encode(CharSequence) encoded} and enclosed in double quotes.
468      * <p>
469      * The map keys must be of type <code>String</code> and values must be objects that implement the <code>CharSequence</code> interface.
470      * <p>
471      * A <code>null</code> value represents an attribute with no value.
472      *
473      * @param attributesMap a map containing attribute name/value pairs.
474      * @return the contents of the specified {@linkplain #populateMap(Map,boolean) attributes map} as HTML attribute name/value pairs.
475      * @see StartTag#generateHTML(String tagName, Map attributesMap, boolean emptyElementTag)
476      */

477     public static String JavaDoc generateHTML(final Map attributesMap) {
478         final StringWriter stringWriter=new StringWriter();
479         try {appendHTML(stringWriter,attributesMap);} catch (IOException ex) {} // IOException never occurs in StringWriter
480
return stringWriter.toString();
481     }
482
483     /**
484      * Returns this instance.
485      * <p>
486      * This method has been deprecated as of version 2.0 as the <code>Attributes</code> class now implements
487      * the <code>List</code> interface, so the instance itself can be used instead.
488      *
489      * @return this instance.
490      * @deprecated Use the {@link Attributes} object itself instead.
491      */

492     public List getList() {
493         return this;
494     }
495
496     /**
497      * Outputs the contents of the specified {@linkplain #populateMap(Map,boolean) attributes map} as HTML attribute name/value pairs to the specified <code>Writer</code>.
498      * <p>
499      * Each attribute is preceded by a single space, and all values are
500      * {@linkplain CharacterReference#encode(CharSequence) encoded} and enclosed in double quotes.
501      *
502      * @param out the <code>Writer</code> to which the output is to be sent.
503      * @param attributesMap a map containing attribute name/value pairs.
504      * @throws IOException if an I/O exception occurs.
505      * @see #populateMap(Map attributesMap, boolean convertNamesToLowerCase)
506      */

507     static void appendHTML(final Writer writer, final Map attributesMap) throws IOException {
508         for (final Iterator i=attributesMap.entrySet().iterator(); i.hasNext();) {
509             final Map.Entry entry=(Map.Entry)i.next();
510             Attribute.appendHTML(writer,(String JavaDoc)entry.getKey(),(CharSequence JavaDoc)entry.getValue());
511         }
512     }
513
514     StringBuffer JavaDoc appendTidy(final StringBuffer JavaDoc sb, Tag nextTag) {
515         for (final Iterator i=listIterator(0); i.hasNext();)
516             nextTag=((Attribute)i.next()).appendTidy(sb,nextTag);
517         return sb;
518     }
519
520     Map getMap(final boolean convertNamesToLowerCase) {
521         return populateMap(new LinkedHashMap(getCount()*2,1.0F),convertNamesToLowerCase);
522     }
523     
524     private static void log(final Source source, final String JavaDoc part1, final CharSequence JavaDoc part2, final int begin, final String JavaDoc part3, final int pos) {
525         source.log(source.getRowColumnVector(pos).appendTo(source.getRowColumnVector(begin).appendTo(new StringBuffer JavaDoc(200).append(part1).append(' ').append(part2).append(" at ")).append(' ').append(part3).append(" at position ")).toString());
526     }
527
528     private static void log(final Source source, final String JavaDoc part1, final CharSequence JavaDoc part2, final int begin, final String JavaDoc part3) {
529         source.log(source.getRowColumnVector(begin).appendTo(new StringBuffer JavaDoc(200).append(part1).append(' ').append(part2).append(" at ")).append(' ').append(part3).toString());
530     }
531 }
532
Popular Tags