1/**
2 * Copyright (c) 2004, Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.mail.lib.html.parser;
17
18import com.android.mail.lib.base.Preconditions;
19
20import java.util.Set;
21
22/**
23 * HTML class defines Element and Attribute classes.
24 *
25 * @author jlim@google.com (Jing Yee Lim)
26 */
27public final class HTML {
28
29  /**
30   * Html element
31   */
32  public static final class Element {
33
34    // TODO(ptucker) other candidate types are list and form elements. Better for this to be
35    // enumerated type.
36    /** Types */
37    public static final int NO_TYPE = 0;
38    public static final int TABLE_TYPE = 1;
39
40    /**
41     * INLINE - charater level elements and text strings
42     * BLOCK  - block-like elements; e.g., paragraphs and lists
43     * NONE   - everything else
44     */
45    public enum Flow {
46      INLINE,
47      BLOCK,
48      NONE
49    }
50
51    private final String name;
52    private final int type;
53    private final boolean empty;
54    private final boolean optionalEndTag;
55    private final boolean breaksFlow;
56    private final Flow flow;
57
58    /**
59     * Construct an Element.
60     *
61     * NOTE: Even though breaksFlow and flow are named similarly, they're not quite the same thing.
62     * Flow refers to whether the element is inherently character or block level. Breaks flow
63     * refers to whether it forces a line break.
64     *
65     * @throws IllegalArgumentException if name or flow is null.
66     */
67    public Element(String name, int type, boolean empty,
68                   boolean optionalEndTag, boolean breaksFlow, Flow flow) {
69      Preconditions.checkNotNull(name, "Element name can not be null");
70      Preconditions.checkNotNull(flow, "Element flow can not be null");
71      this.name = name;
72      this.type = type;
73      this.empty = empty;
74      this.optionalEndTag = optionalEndTag;
75      this.breaksFlow = breaksFlow;
76      this.flow = flow;
77    }
78
79    /**
80     * Construct an Element with inline=true.
81     */
82    public Element(String name, int type, boolean empty,
83                   boolean optionalEndTag, boolean breaksFlow) {
84      this(name, type, empty, optionalEndTag, breaksFlow, Flow.NONE);
85    }
86
87    /** Name of the element, in lowercase, e.g. "a", "br" */
88    public String getName() {
89      return name;
90    }
91
92    /** Type, e.g. TABLE_TYPE */
93    public int getType() {
94      return type;
95    }
96
97    /** True if it's empty, has no inner elements or end tag */
98    public boolean isEmpty() {
99      return empty;
100    }
101
102    /** True if the end tag is optional */
103    public boolean isEndTagOptional() {
104      return optionalEndTag;
105    }
106
107    /**
108     * True if it breaks the flow, and may force a new line before/after the
109     * tag.
110     */
111    public boolean breaksFlow() {
112      return breaksFlow;
113    }
114
115    /** Flow type. */
116    public Flow getFlow() {
117      return flow;
118    }
119
120    /**
121     * @return just name, not proper HTML
122     */
123    @Override
124    public String toString() {
125      return name;
126    }
127
128    @Override
129    public boolean equals(Object o) {
130      if (o == this) {
131        return true;
132      }
133      if (o instanceof HTML.Element) {
134        HTML.Element that = (HTML.Element) o;
135        return this.name.equals(that.name);
136      }
137      return false;
138    }
139
140    @Override
141    public int hashCode() {
142      return this.name.hashCode();
143    }
144  }
145
146  /**
147   * Html attribute
148   */
149  public static final class Attribute {
150    /** Value types */
151    public static final int NO_TYPE = 0;
152    public static final int URI_TYPE = 1;
153    public static final int SCRIPT_TYPE = 2;
154    public static final int ENUM_TYPE = 3;
155    public static final int BOOLEAN_TYPE = 4;
156
157    /** Name of the element, e.g. "HREF" */
158    private final String name;
159
160    /** Type of the attribute value, e.g. URI_TYPE */
161    private final int type;
162
163    /** The list of allowed values, or null if any value is allowed */
164    private final Set<String> values;
165
166    /**
167     * Construct an Attribute
168     * @throws IllegalArgumentException if name is null
169     */
170    public Attribute(String name, int type) {
171      this(name, type, null);
172    }
173
174    /**
175     * Construct an Attribute
176     * @throws IllegalArgumentException if name is null
177     * or if Attribute is of type ENUM_TYPE and the values are null
178     */
179    public Attribute(String name, int type, Set<String> values) {
180      Preconditions.checkNotNull(name, "Attribute name can not be null");
181      Preconditions.checkArgument((values == null) ^ (type == ENUM_TYPE),
182          "Only ENUM_TYPE can have values != null");
183      this.name = name;
184      this.type = type;
185      this.values = values;
186    }
187
188    /** Gets the name of the attribute, in lowercase */
189    public String getName() {
190      return name;
191    }
192
193    /** Gets the type, e.g. URI_TYPE */
194    public int getType() {
195      return type;
196    }
197
198    /**
199     * When called on an attribute of ENUM_TYPE, returns a Set of Strings
200     * containing the allowed attribute values. The return set is guaranteed to
201     * only contain lower case Strings.
202     *
203     * @return a Set of Strings, in lower case, for the allowed attribute
204     *         values.
205     * @throws IllegalStateException if attribute type is not ENUM_TYPE
206     */
207    public Set<String> getEnumValues() {
208      Preconditions.checkState(type == ENUM_TYPE);
209      return values;
210    }
211
212    /**
213     * @return Element name (name only, not proper HTML).
214     */
215    @Override
216    public String toString() {
217      return name;
218    }
219
220    @Override
221    public boolean equals(Object o) {
222      if (o == this) {
223        return true;
224      }
225      if (o instanceof HTML.Attribute) {
226        HTML.Attribute that = (HTML.Attribute) o;
227        return this.name.equals(that.name);
228      }
229      return false;
230    }
231
232    @Override
233    public int hashCode() {
234      return this.name.hashCode();
235    }
236  }
237}