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