1993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira/**
2993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * Copyright (c) 2004, Google Inc.
3993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira *
4993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * Licensed under the Apache License, Version 2.0 (the "License");
5993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * you may not use this file except in compliance with the License.
6993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * You may obtain a copy of the License at
7993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira *
8993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira *     http://www.apache.org/licenses/LICENSE-2.0
9993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira *
10993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * Unless required by applicable law or agreed to in writing, software
11993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * distributed under the License is distributed on an "AS IS" BASIS,
12993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * See the License for the specific language governing permissions and
14993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * limitations under the License.
15993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira */
16993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
171bdbfefe4b144c7b031a1d9242a0fa061a0ae6b5Scott Kennedypackage com.google.android.mail.common.html.parser;
18993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
191bdbfefe4b144c7b031a1d9242a0fa061a0ae6b5Scott Kennedyimport com.google.android.mail.common.base.Preconditions;
20993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
21993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereiraimport java.util.Set;
22993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
23993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira/**
24993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * HTML class defines Element and Attribute classes.
25993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira *
26993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * @author jlim@google.com (Jing Yee Lim)
27993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira */
28993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereirapublic final class HTML {
29993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
30993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  /**
31993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * Html element
32993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   */
33993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  public static final class Element {
34993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
35993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    // TODO(ptucker) other candidate types are list and form elements. Better for this to be
36993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    // enumerated type.
37993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    /** Types */
38993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    public static final int NO_TYPE = 0;
39993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    public static final int TABLE_TYPE = 1;
40993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
41993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    /**
42993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     * INLINE - charater level elements and text strings
43993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     * BLOCK  - block-like elements; e.g., paragraphs and lists
44993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     * NONE   - everything else
45993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     */
46993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    public enum Flow {
47993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      INLINE,
48993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      BLOCK,
49993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      NONE
50993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    }
51993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
52993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    private final String name;
53993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    private final int type;
54993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    private final boolean empty;
55993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    private final boolean optionalEndTag;
56993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    private final boolean breaksFlow;
57993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    private final Flow flow;
58993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
59993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    /**
60993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     * Construct an Element.
61993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     *
62993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     * NOTE: Even though breaksFlow and flow are named similarly, they're not quite the same thing.
63993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     * Flow refers to whether the element is inherently character or block level. Breaks flow
64993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     * refers to whether it forces a line break.
65993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     *
66993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     * @throws IllegalArgumentException if name or flow is null.
67993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     */
68993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    public Element(String name, int type, boolean empty,
69993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira                   boolean optionalEndTag, boolean breaksFlow, Flow flow) {
70993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      Preconditions.checkNotNull(name, "Element name can not be null");
71993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      Preconditions.checkNotNull(flow, "Element flow can not be null");
72993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      this.name = name;
73993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      this.type = type;
74993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      this.empty = empty;
75993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      this.optionalEndTag = optionalEndTag;
76993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      this.breaksFlow = breaksFlow;
77993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      this.flow = flow;
78993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    }
79993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
80993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    /**
81993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     * Construct an Element with inline=true.
82993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     */
83993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    public Element(String name, int type, boolean empty,
84993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira                   boolean optionalEndTag, boolean breaksFlow) {
85993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      this(name, type, empty, optionalEndTag, breaksFlow, Flow.NONE);
86993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    }
87993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
88993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    /** Name of the element, in lowercase, e.g. "a", "br" */
89993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    public String getName() {
90993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      return name;
91993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    }
92993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
93993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    /** Type, e.g. TABLE_TYPE */
94993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    public int getType() {
95993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      return type;
96993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    }
97993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
98993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    /** True if it's empty, has no inner elements or end tag */
99993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    public boolean isEmpty() {
100993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      return empty;
101993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    }
102993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
103993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    /** True if the end tag is optional */
104993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    public boolean isEndTagOptional() {
105993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      return optionalEndTag;
106993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    }
107993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
108993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    /**
109993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     * True if it breaks the flow, and may force a new line before/after the
110993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     * tag.
111993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     */
112993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    public boolean breaksFlow() {
113993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      return breaksFlow;
114993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    }
115993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
116993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    /** Flow type. */
117993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    public Flow getFlow() {
118993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      return flow;
119993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    }
120993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
121993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    /**
122993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     * @return just name, not proper HTML
123993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     */
124993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    @Override
125993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    public String toString() {
126993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      return name;
127993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    }
128993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
129993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    @Override
130993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    public boolean equals(Object o) {
131993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      if (o == this) {
132993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira        return true;
133993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      }
134993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      if (o instanceof HTML.Element) {
135993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira        HTML.Element that = (HTML.Element) o;
136993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira        return this.name.equals(that.name);
137993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      }
138993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      return false;
139993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    }
140993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
141993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    @Override
142993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    public int hashCode() {
143993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      return this.name.hashCode();
144993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    }
145993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  }
146993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
147993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  /**
148993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * Html attribute
149993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   */
150993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  public static final class Attribute {
151993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    /** Value types */
152993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    public static final int NO_TYPE = 0;
153993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    public static final int URI_TYPE = 1;
154993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    public static final int SCRIPT_TYPE = 2;
155993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    public static final int ENUM_TYPE = 3;
156993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    public static final int BOOLEAN_TYPE = 4;
157993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
158993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    /** Name of the element, e.g. "HREF" */
159993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    private final String name;
160993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
161993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    /** Type of the attribute value, e.g. URI_TYPE */
162993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    private final int type;
163993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
164993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    /** The list of allowed values, or null if any value is allowed */
165993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    private final Set<String> values;
166993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
167993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    /**
168993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     * Construct an Attribute
169993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     * @throws IllegalArgumentException if name is null
170993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     */
171993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    public Attribute(String name, int type) {
172993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      this(name, type, null);
173993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    }
174993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
175993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    /**
176993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     * Construct an Attribute
177993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     * @throws IllegalArgumentException if name is null
178993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     * or if Attribute is of type ENUM_TYPE and the values are null
179993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     */
180993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    public Attribute(String name, int type, Set<String> values) {
181993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      Preconditions.checkNotNull(name, "Attribute name can not be null");
182993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      Preconditions.checkArgument((values == null) ^ (type == ENUM_TYPE),
183993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira          "Only ENUM_TYPE can have values != null");
184993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      this.name = name;
185993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      this.type = type;
186993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      this.values = values;
187993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    }
188993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
189993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    /** Gets the name of the attribute, in lowercase */
190993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    public String getName() {
191993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      return name;
192993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    }
193993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
194993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    /** Gets the type, e.g. URI_TYPE */
195993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    public int getType() {
196993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      return type;
197993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    }
198993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
199993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    /**
200993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     * When called on an attribute of ENUM_TYPE, returns a Set of Strings
201993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     * containing the allowed attribute values. The return set is guaranteed to
202993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     * only contain lower case Strings.
203993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     *
204993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     * @return a Set of Strings, in lower case, for the allowed attribute
205993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     *         values.
206993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     * @throws IllegalStateException if attribute type is not ENUM_TYPE
207993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     */
208993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    public Set<String> getEnumValues() {
209993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      Preconditions.checkState(type == ENUM_TYPE);
210993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      return values;
211993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    }
212993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
213993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    /**
214993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     * @return Element name (name only, not proper HTML).
215993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     */
216993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    @Override
217993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    public String toString() {
218993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      return name;
219993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    }
220993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
221993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    @Override
222993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    public boolean equals(Object o) {
223993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      if (o == this) {
224993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira        return true;
225993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      }
226993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      if (o instanceof HTML.Attribute) {
227993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira        HTML.Attribute that = (HTML.Attribute) o;
228993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira        return this.name.equals(that.name);
229993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      }
230993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      return false;
231993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    }
232993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
233993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    @Override
234993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    public int hashCode() {
235993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      return this.name.hashCode();
236993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    }
237993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  }
238993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira}