View Javadoc

1   /*
2    *  Java HTML Tidy - JTidy
3    *  HTML parser and pretty printer
4    *
5    *  Copyright (c) 1998-2000 World Wide Web Consortium (Massachusetts
6    *  Institute of Technology, Institut National de Recherche en
7    *  Informatique et en Automatique, Keio University). All Rights
8    *  Reserved.
9    *
10   *  Contributing Author(s):
11   *
12   *     Dave Raggett <dsr@w3.org>
13   *     Andy Quick <ac.quick@sympatico.ca> (translation to Java)
14   *     Gary L Peskin <garyp@firstech.com> (Java development)
15   *     Sami Lempinen <sami@lempinen.net> (release management)
16   *     Fabrizio Giustina <fgiust at users.sourceforge.net>
17   *
18   *  The contributing author(s) would like to thank all those who
19   *  helped with testing, bug fixes, and patience.  This wouldn't
20   *  have been possible without all of you.
21   *
22   *  COPYRIGHT NOTICE:
23   * 
24   *  This software and documentation is provided "as is," and
25   *  the copyright holders and contributing author(s) make no
26   *  representations or warranties, express or implied, including
27   *  but not limited to, warranties of merchantability or fitness
28   *  for any particular purpose or that the use of the software or
29   *  documentation will not infringe any third party patents,
30   *  copyrights, trademarks or other rights. 
31   *
32   *  The copyright holders and contributing author(s) will not be
33   *  liable for any direct, indirect, special or consequential damages
34   *  arising out of any use of the software or documentation, even if
35   *  advised of the possibility of such damage.
36   *
37   *  Permission is hereby granted to use, copy, modify, and distribute
38   *  this source code, or portions hereof, documentation and executables,
39   *  for any purpose, without fee, subject to the following restrictions:
40   *
41   *  1. The origin of this source code must not be misrepresented.
42   *  2. Altered versions must be plainly marked as such and must
43   *     not be misrepresented as being the original source.
44   *  3. This Copyright notice may not be removed or altered from any
45   *     source or altered source distribution.
46   * 
47   *  The copyright holders and contributing author(s) specifically
48   *  permit, without fee, and encourage the use of this source code
49   *  as a component for supporting the Hypertext Markup Language in
50   *  commercial products. If you use this source code in a product,
51   *  acknowledgment is not required but would be appreciated.
52   *
53   */
54  package org.w3c.tidy;
55  
56  import java.util.HashMap;
57  import java.util.Iterator;
58  import java.util.Map;
59  
60  
61  /**
62   * Check attribute values implementations.
63   * @author Dave Raggett <a href="mailto:dsr@w3.org">dsr@w3.org </a>
64   * @author Andy Quick <a href="mailto:ac.quick@sympatico.ca">ac.quick@sympatico.ca </a> (translation to Java)
65   * @author Fabrizio Giustina
66   * @version $Revision: 779 $ ($Author: fgiust $)
67   */
68  public final class AttrCheckImpl
69  {
70  
71      /**
72       * checker for URLs.
73       */
74      public static final AttrCheck URL = new CheckUrl();
75  
76      /**
77       * checker for scripts.
78       */
79      public static final AttrCheck SCRIPT = new CheckScript();
80  
81      /**
82       * checker for "name" attribute.
83       */
84      public static final AttrCheck NAME = new CheckName();
85  
86      /**
87       * checker for ids.
88       */
89      public static final AttrCheck ID = new CheckId();
90  
91      /**
92       * checker for "align" attribute.
93       */
94      public static final AttrCheck ALIGN = new CheckAlign();
95  
96      /**
97       * checker for "valign" attribute.
98       */
99      public static final AttrCheck VALIGN = new CheckValign();
100 
101     /**
102      * checker for boolean attributes.
103      */
104     public static final AttrCheck BOOL = new CheckBool();
105 
106     /**
107      * checker for "lenght" attribute.
108      */
109     public static final AttrCheck LENGTH = new CheckLength();
110 
111     /**
112      * checker for "target" attribute.
113      */
114     public static final AttrCheck TARGET = new CheckTarget();
115 
116     /**
117      * checker for "submit" attribute.
118      */
119     public static final AttrCheck FSUBMIT = new CheckFsubmit();
120 
121     /**
122      * checker for "clear" attribute.
123      */
124     public static final AttrCheck CLEAR = new CheckClear();
125 
126     /**
127      * checker for "shape" attribute.
128      */
129     public static final AttrCheck SHAPE = new CheckShape();
130 
131     /**
132      * checker for "number" attribute.
133      */
134     public static final AttrCheck NUMBER = new CheckNumber();
135 
136     /**
137      * checker for "scope" attribute.
138      */
139     public static final AttrCheck SCOPE = new CheckScope();
140 
141     /**
142      * checker for "color" attribute.
143      */
144     public static final AttrCheck COLOR = new CheckColor();
145 
146     /**
147      * checker for "vtype" attribute.
148      */
149     public static final AttrCheck VTYPE = new CheckVType();
150 
151     /**
152      * checker for "scroll" attribute.
153      */
154     public static final AttrCheck SCROLL = new CheckScroll();
155 
156     /**
157      * checker for "dir" attribute.
158      */
159     public static final AttrCheck TEXTDIR = new CheckTextDir();
160 
161     /**
162      * checker for "lang" and "xml:lang" attributes.
163      */
164     public static final AttrCheck LANG = new CheckLang();
165 
166     /**
167      * checker for text attributes. Actually null (no validation).
168      */
169     public static final AttrCheck TEXT = null;
170 
171     /**
172      * checker for "charset" attribute. Actually null (no validation).
173      */
174     public static final AttrCheck CHARSET = null;
175 
176     /**
177      * checker for "type" attribute. Actually null (no validation).
178      */
179     public static final AttrCheck TYPE = null;
180 
181     /**
182      * checker for attributes that can contain a single character. Actually null (no validation).
183      */
184     public static final AttrCheck CHARACTER = null;
185 
186     /**
187      * checker for attributes which contain a list of urls. Actually null (no validation).
188      */
189     public static final AttrCheck URLS = null;
190 
191     /**
192      * checker for "cols" attribute. Actually null (no validation).
193      */
194     public static final AttrCheck COLS = null;
195 
196     /**
197      * checker for "coords" attribute. Actually null (no validation).
198      */
199     public static final AttrCheck COORDS = null;
200 
201     /**
202      * checker for attributes containing dates. Actually null (no validation).
203      */
204     public static final AttrCheck DATE = null;
205 
206     /**
207      * checker for attributes referencng an id. Actually null (no validation).
208      */
209     public static final AttrCheck IDREF = null;
210 
211     /**
212      * checker for table "frame" attribute. Actually null (no validation).
213      */
214     public static final AttrCheck TFRAME = null;
215 
216     /**
217      * checker for "frameborder" attribute. Actually null (no validation).
218      */
219     public static final AttrCheck FBORDER = null;
220 
221     /**
222      * checker for "media" attribute. Actually null (no validation).
223      */
224     public static final AttrCheck MEDIA = null;
225 
226     /**
227      * checker for "rel" and "rev" attributes. Actually null (no validation).
228      */
229     public static final AttrCheck LINKTYPES = null;
230 
231     /**
232      * checker for table "rules" attribute. Actually null (no validation).
233      */
234     public static final AttrCheck TRULES = null;
235 
236     /**
237      * utility class, don't instantiate.
238      */
239     private AttrCheckImpl()
240     {
241         // empty private constructor
242     }
243 
244     /**
245      * AttrCheck implementation for checking URLs.
246      */
247     public static class CheckUrl implements AttrCheck
248     {
249 
250         /**
251          * @see AttrCheck#check(Lexer, Node, AttVal)
252          */
253         public void check(Lexer lexer, Node node, AttVal attval)
254         {
255             char c;
256             StringBuffer dest;
257             boolean escapeFound = false;
258             boolean backslashFound = false;
259             int i = 0;
260 
261             if (attval.value == null)
262             {
263                 lexer.report.attrError(lexer, node, attval, Report.MISSING_ATTR_VALUE);
264                 return;
265             }
266 
267             String p = attval.value;
268 
269             for (i = 0; i < p.length(); ++i)
270             {
271                 c = p.charAt(i);
272                 // find \
273                 if (c == '\\')
274                 {
275                     backslashFound = true;
276                 }
277                 // find non-ascii chars
278                 else if ((c > 0x7e) || (c <= 0x20) || (c == '<') || (c == '>'))
279                 {
280                     escapeFound = true;
281                 }
282             }
283 
284             // backslashes found, fix them
285             if (lexer.configuration.fixBackslash && backslashFound)
286             {
287                 attval.value = attval.value.replace('\\', '/');
288                 p = attval.value;
289             }
290 
291             // non-ascii chars found, fix them
292             if (lexer.configuration.fixUri && escapeFound)
293             {
294                 dest = new StringBuffer();
295 
296                 for (i = 0; i < p.length(); ++i)
297                 {
298                     c = p.charAt(i);
299                     if ((c > 0x7e) || (c <= 0x20) || (c == '<') || (c == '>'))
300                     {
301                         dest.append('%');
302                         dest.append(Integer.toHexString(c).toUpperCase());
303                     }
304                     else
305                     {
306                         dest.append(c);
307                     }
308                 }
309 
310                 attval.value = dest.toString();
311             }
312             if (backslashFound)
313             {
314                 if (lexer.configuration.fixBackslash)
315                 {
316                     lexer.report.attrError(lexer, node, attval, Report.FIXED_BACKSLASH);
317                 }
318                 else
319                 {
320                     lexer.report.attrError(lexer, node, attval, Report.BACKSLASH_IN_URI);
321                 }
322             }
323             if (escapeFound)
324             {
325                 if (lexer.configuration.fixUri)
326                 {
327                     lexer.report.attrError(lexer, node, attval, Report.ESCAPED_ILLEGAL_URI);
328                 }
329                 else
330                 {
331                     lexer.report.attrError(lexer, node, attval, Report.ILLEGAL_URI_REFERENCE);
332                 }
333 
334                 lexer.badChars |= Report.INVALID_URI;
335             }
336 
337         }
338     }
339 
340     /**
341      * AttrCheck implementation for checking scripts.
342      */
343     public static class CheckScript implements AttrCheck
344     {
345 
346         /**
347          * @see AttrCheck#check(Lexer, Node, AttVal)
348          */
349         public void check(Lexer lexer, Node node, AttVal attval)
350         {
351             // not implemented
352         }
353 
354     }
355 
356     /**
357      * AttrCheck implementation for checking the "align" attribute.
358      */
359     public static class CheckAlign implements AttrCheck
360     {
361 
362         /**
363          * valid values for this attribute.
364          */
365         private static final String[] VALID_VALUES = new String[]{"left", "center", "right", "justify"};
366 
367         /**
368          * @see AttrCheck#check(Lexer, Node, AttVal)
369          */
370         public void check(Lexer lexer, Node node, AttVal attval)
371         {
372             // IMG, OBJECT, APPLET and EMBED use align for vertical position
373             if (node.tag != null && ((node.tag.model & Dict.CM_IMG) != 0))
374             {
375                 VALIGN.check(lexer, node, attval);
376                 return;
377             }
378 
379             if (attval.value == null)
380             {
381                 lexer.report.attrError(lexer, node, attval, Report.MISSING_ATTR_VALUE);
382                 return;
383             }
384 
385             attval.checkLowerCaseAttrValue(lexer, node);
386 
387             if (!TidyUtils.isInValuesIgnoreCase(VALID_VALUES, attval.value))
388             {
389                 lexer.report.attrError(lexer, node, attval, Report.BAD_ATTRIBUTE_VALUE);
390             }
391         }
392 
393     }
394 
395     /**
396      * AttrCheck implementation for checking the "valign" attribute.
397      */
398     public static class CheckValign implements AttrCheck
399     {
400 
401         /**
402          * valid values for this attribute.
403          */
404         private static final String[] VALID_VALUES = new String[]{"top", "middle", "bottom", "baseline"};
405 
406         /**
407          * valid values for this attribute (only for img tag).
408          */
409         private static final String[] VALID_VALUES_IMG = new String[]{"left", "right"};
410 
411         /**
412          * proprietary values for this attribute.
413          */
414         private static final String[] VALID_VALUES_PROPRIETARY = new String[]{
415             "texttop",
416             "absmiddle",
417             "absbottom",
418             "textbottom"};
419 
420         /**
421          * @see AttrCheck#check(Lexer, Node, AttVal)
422          */
423         public void check(Lexer lexer, Node node, AttVal attval)
424         {
425             String value;
426 
427             if (attval.value == null)
428             {
429                 lexer.report.attrError(lexer, node, attval, Report.MISSING_ATTR_VALUE);
430                 return;
431             }
432 
433             attval.checkLowerCaseAttrValue(lexer, node);
434 
435             value = attval.value;
436 
437             if (TidyUtils.isInValuesIgnoreCase(VALID_VALUES, value))
438             {
439                 // all is fine
440                 return;
441             }
442 
443             if (TidyUtils.isInValuesIgnoreCase(VALID_VALUES_IMG, value))
444             {
445                 if (!(node.tag != null && ((node.tag.model & Dict.CM_IMG) != 0)))
446                 {
447                     lexer.report.attrError(lexer, node, attval, Report.BAD_ATTRIBUTE_VALUE);
448                 }
449             }
450             else if (TidyUtils.isInValuesIgnoreCase(VALID_VALUES_PROPRIETARY, value))
451             {
452                 lexer.constrainVersion(Dict.VERS_PROPRIETARY);
453                 lexer.report.attrError(lexer, node, attval, Report.PROPRIETARY_ATTR_VALUE);
454             }
455             else
456             {
457                 lexer.report.attrError(lexer, node, attval, Report.BAD_ATTRIBUTE_VALUE);
458             }
459         }
460 
461     }
462 
463     /**
464      * AttrCheck implementation for checking boolean attributes.
465      */
466     public static class CheckBool implements AttrCheck
467     {
468 
469         /**
470          * @see AttrCheck#check(Lexer, Node, AttVal)
471          */
472         public void check(Lexer lexer, Node node, AttVal attval)
473         {
474             if (attval.value == null)
475             {
476                 return;
477             }
478 
479             attval.checkLowerCaseAttrValue(lexer, node);
480         }
481 
482     }
483 
484     /**
485      * AttrCheck implementation for checking the "length" attribute.
486      */
487     public static class CheckLength implements AttrCheck
488     {
489 
490         /**
491          * @see AttrCheck#check(Lexer, Node, AttVal)
492          */
493         public void check(Lexer lexer, Node node, AttVal attval)
494         {
495 
496             if (attval.value == null)
497             {
498                 lexer.report.attrError(lexer, node, attval, Report.MISSING_ATTR_VALUE);
499                 return;
500             }
501 
502             // don't check for <col width=...> and <colgroup width=...>
503             if ("width".equalsIgnoreCase(attval.attribute)
504                 && (node.tag == lexer.configuration.tt.tagCol || node.tag == lexer.configuration.tt.tagColgroup))
505             {
506                 return;
507             }
508 
509             String p = attval.value;
510 
511             if (p.length() == 0 || (!Character.isDigit(p.charAt(0)) && !('%' == p.charAt(0))))
512             {
513                 lexer.report.attrError(lexer, node, attval, Report.BAD_ATTRIBUTE_VALUE);
514             }
515             else
516             {
517 
518                 TagTable tt = lexer.configuration.tt;
519 
520                 for (int j = 1; j < p.length(); j++)
521                 {
522                     // elements th and td must not use percentages
523                     if ((!Character.isDigit(p.charAt(j)) && (node.tag == tt.tagTd || node.tag == tt.tagTh))
524                         || (!Character.isDigit(p.charAt(j)) && p.charAt(j) != '%'))
525                     {
526                         lexer.report.attrError(lexer, node, attval, Report.BAD_ATTRIBUTE_VALUE);
527                         break;
528                     }
529                 }
530             }
531         }
532     }
533 
534     /**
535      * AttrCheck implementation for checking the "target" attribute.
536      */
537     public static class CheckTarget implements AttrCheck
538     {
539 
540         /**
541          * valid values for this attribute.
542          */
543         private static final String[] VALID_VALUES = new String[]{"_blank", "_self", "_parent", "_top"};
544 
545         /**
546          * @see AttrCheck#check(Lexer, Node, AttVal)
547          */
548         public void check(Lexer lexer, Node node, AttVal attval)
549         {
550 
551             // No target attribute in strict HTML versions
552             lexer.constrainVersion(~Dict.VERS_HTML40_STRICT);
553 
554             if (attval.value == null || attval.value.length() == 0)
555             {
556                 lexer.report.attrError(lexer, node, attval, Report.MISSING_ATTR_VALUE);
557                 return;
558             }
559 
560             String value = attval.value;
561 
562             // target names must begin with A-Za-z ...
563             if (Character.isLetter(value.charAt(0)))
564             {
565                 return;
566             }
567 
568             // or be one of _blank, _self, _parent and _top
569             if (!TidyUtils.isInValuesIgnoreCase(VALID_VALUES, value))
570             {
571                 lexer.report.attrError(lexer, node, attval, Report.BAD_ATTRIBUTE_VALUE);
572             }
573 
574         }
575     }
576 
577     /**
578      * AttrCheck implementation for checking the "submit" attribute.
579      */
580     public static class CheckFsubmit implements AttrCheck
581     {
582 
583         /**
584          * valid values for this attribute.
585          */
586         private static final String[] VALID_VALUES = new String[]{"get", "post"};
587 
588         /**
589          * @see AttrCheck#check(Lexer, Node, AttVal)
590          */
591         public void check(Lexer lexer, Node node, AttVal attval)
592         {
593             if (attval.value == null)
594             {
595                 lexer.report.attrError(lexer, node, attval, Report.MISSING_ATTR_VALUE);
596                 return;
597             }
598 
599             attval.checkLowerCaseAttrValue(lexer, node);
600 
601             if (!TidyUtils.isInValuesIgnoreCase(VALID_VALUES, attval.value))
602             {
603                 lexer.report.attrError(lexer, node, attval, Report.BAD_ATTRIBUTE_VALUE);
604             }
605         }
606     }
607 
608     /**
609      * AttrCheck implementation for checking the "clear" attribute.
610      */
611     public static class CheckClear implements AttrCheck
612     {
613 
614         /**
615          * valid values for this attribute.
616          */
617         private static final String[] VALID_VALUES = new String[]{"none", "left", "right", "all"};
618 
619         /**
620          * @see AttrCheck#check(Lexer, Node, AttVal)
621          */
622         public void check(Lexer lexer, Node node, AttVal attval)
623         {
624             if (attval.value == null)
625             {
626                 lexer.report.attrError(lexer, node, attval, Report.MISSING_ATTR_VALUE);
627                 attval.value = VALID_VALUES[0];
628                 return;
629             }
630 
631             attval.checkLowerCaseAttrValue(lexer, node);
632 
633             if (!TidyUtils.isInValuesIgnoreCase(VALID_VALUES, attval.value))
634             {
635                 lexer.report.attrError(lexer, node, attval, Report.BAD_ATTRIBUTE_VALUE);
636             }
637 
638         }
639     }
640 
641     /**
642      * AttrCheck implementation for checking the "shape" attribute.
643      */
644     public static class CheckShape implements AttrCheck
645     {
646 
647         /**
648          * valid values for this attribute.
649          */
650         private static final String[] VALID_VALUES = new String[]{"rect", "default", "circle", "poly"};
651 
652         /**
653          * @see AttrCheck#check(Lexer, Node, AttVal)
654          */
655         public void check(Lexer lexer, Node node, AttVal attval)
656         {
657             if (attval.value == null)
658             {
659                 lexer.report.attrError(lexer, node, attval, Report.MISSING_ATTR_VALUE);
660                 return;
661             }
662 
663             attval.checkLowerCaseAttrValue(lexer, node);
664 
665             if (!TidyUtils.isInValuesIgnoreCase(VALID_VALUES, attval.value))
666             {
667                 lexer.report.attrError(lexer, node, attval, Report.BAD_ATTRIBUTE_VALUE);
668             }
669 
670         }
671     }
672 
673     /**
674      * AttrCheck implementation for checking Scope.
675      */
676     public static class CheckScope implements AttrCheck
677     {
678 
679         /**
680          * valid values for this attribute.
681          */
682         private static final String[] VALID_VALUES = new String[]{"row", "rowgroup", "col", "colgroup"};
683 
684         /**
685          * @see AttrCheck#check(Lexer, Node, AttVal)
686          */
687         public void check(Lexer lexer, Node node, AttVal attval)
688         {
689 
690             if (attval.value == null)
691             {
692                 lexer.report.attrError(lexer, node, attval, Report.MISSING_ATTR_VALUE);
693                 return;
694             }
695 
696             attval.checkLowerCaseAttrValue(lexer, node);
697 
698             if (!TidyUtils.isInValuesIgnoreCase(VALID_VALUES, attval.value))
699             {
700                 lexer.report.attrError(lexer, node, attval, Report.BAD_ATTRIBUTE_VALUE);
701             }
702         }
703     }
704 
705     /**
706      * AttrCheck implementation for checking numbers.
707      */
708     public static class CheckNumber implements AttrCheck
709     {
710 
711         /**
712          * @see AttrCheck#check(Lexer, Node, AttVal)
713          */
714         public void check(Lexer lexer, Node node, AttVal attval)
715         {
716 
717             if (attval.value == null)
718             {
719                 lexer.report.attrError(lexer, node, attval, Report.MISSING_ATTR_VALUE);
720                 return;
721             }
722 
723             // don't check <frameset cols=... rows=...>
724             if (("cols".equalsIgnoreCase(attval.attribute) || "rows".equalsIgnoreCase(attval.attribute))
725                 && node.tag == lexer.configuration.tt.tagFrameset)
726             {
727                 return;
728             }
729 
730             String value = attval.value;
731 
732             int j = 0;
733 
734             // font size may be preceded by + or -
735             if (node.tag == lexer.configuration.tt.tagFont && (value.startsWith("+") || value.startsWith("-")))
736             {
737                 ++j;
738             }
739 
740             for (; j < value.length(); j++)
741             {
742                 char p = value.charAt(j);
743                 if (!Character.isDigit(p))
744                 {
745                     lexer.report.attrError(lexer, node, attval, Report.BAD_ATTRIBUTE_VALUE);
746                     break;
747                 }
748             }
749         }
750     }
751 
752     /**
753      * AttrCheck implementation for checking ids.
754      */
755     public static class CheckId implements AttrCheck
756     {
757 
758         /**
759          * @see AttrCheck#check(Lexer, Node, AttVal)
760          */
761         public void check(Lexer lexer, Node node, AttVal attval)
762         {
763             Node old;
764 
765             if (attval.value == null || attval.value.length() == 0)
766             {
767                 lexer.report.attrError(lexer, node, attval, Report.MISSING_ATTR_VALUE);
768                 return;
769             }
770 
771             String p = attval.value;
772             char s = p.charAt(0);
773 
774             if (p.length() == 0 || !Character.isLetter(p.charAt(0)))
775             {
776                 if (lexer.isvoyager && (TidyUtils.isXMLLetter(s) || s == '_' || s == ':'))
777                 {
778                     lexer.report.attrError(lexer, node, attval, Report.XML_ID_SYNTAX);
779                 }
780                 else
781                 {
782                     lexer.report.attrError(lexer, node, attval, Report.BAD_ATTRIBUTE_VALUE);
783                 }
784             }
785             else
786             {
787 
788                 for (int j = 1; j < p.length(); j++)
789                 {
790                     s = p.charAt(j);
791 
792                     if (!TidyUtils.isNamechar(s))
793                     {
794                         if (lexer.isvoyager && TidyUtils.isXMLNamechar(s))
795                         {
796                             lexer.report.attrError(lexer, node, attval, Report.XML_ID_SYNTAX);
797                         }
798                         else
799                         {
800                             lexer.report.attrError(lexer, node, attval, Report.BAD_ATTRIBUTE_VALUE);
801                         }
802                         break;
803                     }
804                 }
805             }
806 
807             if (((old = lexer.configuration.tt.getNodeByAnchor(attval.value)) != null) && old != node)
808             {
809                 lexer.report.attrError(lexer, node, attval, Report.ANCHOR_NOT_UNIQUE);
810             }
811             else
812             {
813                 lexer.configuration.tt.anchorList = lexer.configuration.tt.addAnchor(attval.value, node);
814             }
815         }
816 
817     }
818 
819     /**
820      * AttrCheck implementation for checking the "name" attribute.
821      */
822     public static class CheckName implements AttrCheck
823     {
824 
825         /**
826          * @see AttrCheck#check(Lexer, Node, AttVal)
827          */
828         public void check(Lexer lexer, Node node, AttVal attval)
829         {
830             Node old;
831 
832             if (attval.value == null)
833             {
834                 lexer.report.attrError(lexer, node, attval, Report.MISSING_ATTR_VALUE);
835                 return;
836             }
837             else if (lexer.configuration.tt.isAnchorElement(node))
838             {
839                 lexer.constrainVersion(~Dict.VERS_XHTML11);
840 
841                 if (((old = lexer.configuration.tt.getNodeByAnchor(attval.value)) != null) && old != node)
842                 {
843                     lexer.report.attrError(lexer, node, attval, Report.ANCHOR_NOT_UNIQUE);
844                 }
845                 else
846                 {
847                     lexer.configuration.tt.anchorList = lexer.configuration.tt.addAnchor(attval.value, node);
848                 }
849             }
850         }
851 
852     }
853 
854     /**
855      * AttrCheck implementation for checking colors.
856      */
857     public static class CheckColor implements AttrCheck
858     {
859 
860         /**
861          * valid html colors.
862          */
863         private static final Map COLORS = new HashMap();
864 
865         static
866         {
867             COLORS.put("black", "#000000");
868             COLORS.put("green", "#008000");
869             COLORS.put("silver", "#C0C0C0");
870             COLORS.put("lime", "#00FF00");
871             COLORS.put("gray", "#808080");
872             COLORS.put("olive", "#808000");
873             COLORS.put("white", "#FFFFFF");
874             COLORS.put("yellow", "#FFFF00");
875             COLORS.put("maroon", "#800000");
876             COLORS.put("navy", "#000080");
877             COLORS.put("red", "#FF0000");
878             COLORS.put("blue", "#0000FF");
879             COLORS.put("purple", "#800080");
880             COLORS.put("teal", "#008080");
881             COLORS.put("fuchsia", "#FF00FF");
882             COLORS.put("aqua", "#00FFFF");
883         }
884 
885         /**
886          * @see AttrCheck#check(Lexer, Node, AttVal)
887          */
888         public void check(Lexer lexer, Node node, AttVal attval)
889         {
890             boolean hexUppercase = true;
891             boolean invalid = false;
892             boolean found = false;
893 
894             if (attval.value == null || attval.value.length() == 0)
895             {
896                 lexer.report.attrError(lexer, node, attval, Report.MISSING_ATTR_VALUE);
897                 return;
898             }
899 
900             String given = attval.value;
901 
902             Iterator colorIter = COLORS.entrySet().iterator();
903 
904             while (colorIter.hasNext())
905             {
906                 Map.Entry color = (Map.Entry) colorIter.next();
907 
908                 if (given.charAt(0) == '#')
909                 {
910                     if (given.length() != 7)
911                     {
912                         lexer.report.attrError(lexer, node, attval, Report.BAD_ATTRIBUTE_VALUE);
913                         invalid = true;
914                         break;
915                     }
916                     else if (given.equalsIgnoreCase((String) color.getValue()))
917                     {
918                         if (lexer.configuration.replaceColor)
919                         {
920                             attval.value = (String) color.getKey();
921                         }
922                         found = true;
923                         break;
924                     }
925                 }
926                 else if (TidyUtils.isLetter(given.charAt(0)))
927                 {
928                     if (given.equalsIgnoreCase((String) color.getKey()))
929                     {
930                         if (lexer.configuration.replaceColor)
931                         {
932                             attval.value = (String) color.getKey();
933                         }
934                         found = true;
935                         break;
936                     }
937                 }
938                 else
939                 {
940 
941                     lexer.report.attrError(lexer, node, attval, Report.BAD_ATTRIBUTE_VALUE);
942 
943                     invalid = true;
944                     break;
945                 }
946             }
947             if (!found && !invalid)
948             {
949                 if (given.charAt(0) == '#')
950                 {
951                     // check if valid hex digits and letters
952 
953                     for (int i = 1; i < 7; ++i)
954                     {
955                         if (!TidyUtils.isDigit(given.charAt(i))
956                             && ("abcdef".indexOf(Character.toLowerCase(given.charAt(i))) == -1))
957                         {
958                             lexer.report.attrError(lexer, node, attval, Report.BAD_ATTRIBUTE_VALUE);
959                             invalid = true;
960                             break;
961                         }
962                     }
963                     // convert hex letters to uppercase
964                     if (!invalid && hexUppercase)
965                     {
966                         for (int i = 1; i < 7; ++i)
967                         {
968                             attval.value = given.toUpperCase();
969                         }
970                     }
971                 }
972 
973                 else
974                 {
975                     // we could search for more colors and mark the file as HTML Proprietary, but I don't thinks
976                     // it's worth the effort, so values not in HTML 4.01 are invalid
977                     lexer.report.attrError(lexer, node, attval, Report.BAD_ATTRIBUTE_VALUE);
978                     invalid = true;
979                 }
980             }
981         }
982     }
983 
984     /**
985      * AttrCheck implementation for checking valuetype.
986      */
987     public static class CheckVType implements AttrCheck
988     {
989 
990         /**
991          * valid values for this attribute.
992          */
993         private static final String[] VALID_VALUES = new String[]{"data", "object", "ref"};
994 
995         /**
996          * @see AttrCheck#check(Lexer, Node, AttVal)
997          */
998         public void check(Lexer lexer, Node node, AttVal attval)
999         {
1000             if (attval.value == null)
1001             {
1002                 lexer.report.attrError(lexer, node, attval, Report.MISSING_ATTR_VALUE);
1003                 return;
1004             }
1005 
1006             attval.checkLowerCaseAttrValue(lexer, node);
1007 
1008             if (!TidyUtils.isInValuesIgnoreCase(VALID_VALUES, attval.value))
1009             {
1010                 lexer.report.attrError(lexer, node, attval, Report.BAD_ATTRIBUTE_VALUE);
1011             }
1012         }
1013     }
1014 
1015     /**
1016      * AttrCheck implementation for checking scroll.
1017      */
1018     public static class CheckScroll implements AttrCheck
1019     {
1020 
1021         /**
1022          * valid values for this attribute.
1023          */
1024         private static final String[] VALID_VALUES = new String[]{"no", "yes", "auto"};
1025 
1026         /**
1027          * @see AttrCheck#check(Lexer, Node, AttVal)
1028          */
1029         public void check(Lexer lexer, Node node, AttVal attval)
1030         {
1031 
1032             if (attval.value == null)
1033             {
1034                 lexer.report.attrError(lexer, node, attval, Report.MISSING_ATTR_VALUE);
1035                 return;
1036             }
1037 
1038             attval.checkLowerCaseAttrValue(lexer, node);
1039 
1040             if (!TidyUtils.isInValuesIgnoreCase(VALID_VALUES, attval.value))
1041             {
1042                 lexer.report.attrError(lexer, node, attval, Report.BAD_ATTRIBUTE_VALUE);
1043             }
1044         }
1045     }
1046 
1047     /**
1048      * AttrCheck implementation for checking dir.
1049      */
1050     public static class CheckTextDir implements AttrCheck
1051     {
1052 
1053         /**
1054          * valid values for this attribute.
1055          */
1056         private static final String[] VALID_VALUES = new String[]{"rtl", "ltr"};
1057 
1058         /**
1059          * @see AttrCheck#check(Lexer, Node, AttVal)
1060          */
1061         public void check(Lexer lexer, Node node, AttVal attval)
1062         {
1063 
1064             if (attval.value == null)
1065             {
1066                 lexer.report.attrError(lexer, node, attval, Report.MISSING_ATTR_VALUE);
1067                 return;
1068             }
1069 
1070             attval.checkLowerCaseAttrValue(lexer, node);
1071 
1072             if (!TidyUtils.isInValuesIgnoreCase(VALID_VALUES, attval.value))
1073             {
1074                 lexer.report.attrError(lexer, node, attval, Report.BAD_ATTRIBUTE_VALUE);
1075             }
1076         }
1077     }
1078 
1079     /**
1080      * AttrCheck implementation for checking lang and xml:lang.
1081      */
1082     public static class CheckLang implements AttrCheck
1083     {
1084 
1085         /**
1086          * @see AttrCheck#check(Lexer, Node, AttVal)
1087          */
1088         public void check(Lexer lexer, Node node, AttVal attval)
1089         {
1090 
1091             if ("lang".equals(attval.attribute))
1092             {
1093                 lexer.constrainVersion(~Dict.VERS_XHTML11);
1094             }
1095 
1096             if (attval.value == null)
1097             {
1098                 lexer.report.attrError(lexer, node, attval, Report.MISSING_ATTR_VALUE);
1099                 return;
1100             }
1101         }
1102     }
1103 
1104 }