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 
17 package com.google.android.mail.common.html.parser;
18 
19 import com.google.android.mail.common.base.Preconditions;
20 
21 import java.util.Set;
22 
23 /**
24  * HTML class defines Element and Attribute classes.
25  *
26  * @author jlim@google.com (Jing Yee Lim)
27  */
28 public 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      */
Element(String name, int type, boolean empty, boolean optionalEndTag, boolean breaksFlow, Flow flow)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      */
Element(String name, int type, boolean empty, boolean optionalEndTag, boolean breaksFlow)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" */
getName()89     public String getName() {
90       return name;
91     }
92 
93     /** Type, e.g. TABLE_TYPE */
getType()94     public int getType() {
95       return type;
96     }
97 
98     /** True if it's empty, has no inner elements or end tag */
isEmpty()99     public boolean isEmpty() {
100       return empty;
101     }
102 
103     /** True if the end tag is optional */
isEndTagOptional()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      */
breaksFlow()112     public boolean breaksFlow() {
113       return breaksFlow;
114     }
115 
116     /** Flow type. */
getFlow()117     public Flow getFlow() {
118       return flow;
119     }
120 
121     /**
122      * @return just name, not proper HTML
123      */
124     @Override
toString()125     public String toString() {
126       return name;
127     }
128 
129     @Override
equals(Object o)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
hashCode()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      */
Attribute(String name, int type)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      */
Attribute(String name, int type, Set<String> values)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 */
getName()190     public String getName() {
191       return name;
192     }
193 
194     /** Gets the type, e.g. URI_TYPE */
getType()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      */
getEnumValues()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
toString()217     public String toString() {
218       return name;
219     }
220 
221     @Override
equals(Object o)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
hashCode()234     public int hashCode() {
235       return this.name.hashCode();
236     }
237   }
238 }