1/**
2 * $RCSfile$
3 * $Revision$
4 * $Date$
5 *
6 * Copyright 2003-2007 Jive Software.
7 *
8 * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 *     http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21package org.jivesoftware.smackx;
22
23import org.jivesoftware.smack.util.StringUtils;
24
25import java.util.ArrayList;
26import java.util.Collections;
27import java.util.Iterator;
28import java.util.List;
29
30/**
31 * Represents a field of a form. The field could be used to represent a question to complete,
32 * a completed question or a data returned from a search. The exact interpretation of the field
33 * depends on the context where the field is used.
34 *
35 * @author Gaston Dombiak
36 */
37public class FormField {
38
39    public static final String TYPE_BOOLEAN = "boolean";
40    public static final String TYPE_FIXED = "fixed";
41    public static final String TYPE_HIDDEN = "hidden";
42    public static final String TYPE_JID_MULTI = "jid-multi";
43    public static final String TYPE_JID_SINGLE = "jid-single";
44    public static final String TYPE_LIST_MULTI = "list-multi";
45    public static final String TYPE_LIST_SINGLE = "list-single";
46    public static final String TYPE_TEXT_MULTI = "text-multi";
47    public static final String TYPE_TEXT_PRIVATE = "text-private";
48    public static final String TYPE_TEXT_SINGLE = "text-single";
49
50    private String description;
51    private boolean required = false;
52    private String label;
53    private String variable;
54    private String type;
55    private final List<Option> options = new ArrayList<Option>();
56    private final List<String> values = new ArrayList<String>();
57
58    /**
59     * Creates a new FormField with the variable name that uniquely identifies the field
60     * in the context of the form.
61     *
62     * @param variable the variable name of the question.
63     */
64    public FormField(String variable) {
65        this.variable = variable;
66    }
67
68    /**
69     * Creates a new FormField of type FIXED. The fields of type FIXED do not define a variable
70     * name.
71     */
72    public FormField() {
73        this.type = FormField.TYPE_FIXED;
74    }
75
76    /**
77     * Returns a description that provides extra clarification about the question. This information
78     * could be presented to the user either in tool-tip, help button, or as a section of text
79     * before the question.<p>
80     * <p/>
81     * If the question is of type FIXED then the description should remain empty.
82     *
83     * @return description that provides extra clarification about the question.
84     */
85    public String getDescription() {
86        return description;
87    }
88
89    /**
90     * Returns the label of the question which should give enough information to the user to
91     * fill out the form.
92     *
93     * @return label of the question.
94     */
95    public String getLabel() {
96        return label;
97    }
98
99    /**
100     * Returns an Iterator for the available options that the user has in order to answer
101     * the question.
102     *
103     * @return Iterator for the available options.
104     */
105    public Iterator<Option> getOptions() {
106        synchronized (options) {
107            return Collections.unmodifiableList(new ArrayList<Option>(options)).iterator();
108        }
109    }
110
111    /**
112     * Returns true if the question must be answered in order to complete the questionnaire.
113     *
114     * @return true if the question must be answered in order to complete the questionnaire.
115     */
116    public boolean isRequired() {
117        return required;
118    }
119
120    /**
121     * Returns an indicative of the format for the data to answer. Valid formats are:
122     * <p/>
123     * <ul>
124     * <li>text-single -> single line or word of text
125     * <li>text-private -> instead of showing the user what they typed, you show ***** to
126     * protect it
127     * <li>text-multi -> multiple lines of text entry
128     * <li>list-single -> given a list of choices, pick one
129     * <li>list-multi -> given a list of choices, pick one or more
130     * <li>boolean -> 0 or 1, true or false, yes or no. Default value is 0
131     * <li>fixed -> fixed for putting in text to show sections, or just advertise your web
132     * site in the middle of the form
133     * <li>hidden -> is not given to the user at all, but returned with the questionnaire
134     * <li>jid-single -> Jabber ID - choosing a JID from your roster, and entering one based
135     * on the rules for a JID.
136     * <li>jid-multi -> multiple entries for JIDs
137     * </ul>
138     *
139     * @return format for the data to answer.
140     */
141    public String getType() {
142        return type;
143    }
144
145    /**
146     * Returns an Iterator for the default values of the question if the question is part
147     * of a form to fill out. Otherwise, returns an Iterator for the answered values of
148     * the question.
149     *
150     * @return an Iterator for the default values or answered values of the question.
151     */
152    public Iterator<String> getValues() {
153        synchronized (values) {
154            return Collections.unmodifiableList(new ArrayList<String>(values)).iterator();
155        }
156    }
157
158    /**
159     * Returns the variable name that the question is filling out.
160     *
161     * @return the variable name of the question.
162     */
163    public String getVariable() {
164        return variable;
165    }
166
167    /**
168     * Sets a description that provides extra clarification about the question. This information
169     * could be presented to the user either in tool-tip, help button, or as a section of text
170     * before the question.<p>
171     * <p/>
172     * If the question is of type FIXED then the description should remain empty.
173     *
174     * @param description provides extra clarification about the question.
175     */
176    public void setDescription(String description) {
177        this.description = description;
178    }
179
180    /**
181     * Sets the label of the question which should give enough information to the user to
182     * fill out the form.
183     *
184     * @param label the label of the question.
185     */
186    public void setLabel(String label) {
187        this.label = label;
188    }
189
190    /**
191     * Sets if the question must be answered in order to complete the questionnaire.
192     *
193     * @param required if the question must be answered in order to complete the questionnaire.
194     */
195    public void setRequired(boolean required) {
196        this.required = required;
197    }
198
199    /**
200     * Sets an indicative of the format for the data to answer. Valid formats are:
201     * <p/>
202     * <ul>
203     * <li>text-single -> single line or word of text
204     * <li>text-private -> instead of showing the user what they typed, you show ***** to
205     * protect it
206     * <li>text-multi -> multiple lines of text entry
207     * <li>list-single -> given a list of choices, pick one
208     * <li>list-multi -> given a list of choices, pick one or more
209     * <li>boolean -> 0 or 1, true or false, yes or no. Default value is 0
210     * <li>fixed -> fixed for putting in text to show sections, or just advertise your web
211     * site in the middle of the form
212     * <li>hidden -> is not given to the user at all, but returned with the questionnaire
213     * <li>jid-single -> Jabber ID - choosing a JID from your roster, and entering one based
214     * on the rules for a JID.
215     * <li>jid-multi -> multiple entries for JIDs
216     * </ul>
217     *
218     * @param type an indicative of the format for the data to answer.
219     */
220    public void setType(String type) {
221        this.type = type;
222    }
223
224    /**
225     * Adds a default value to the question if the question is part of a form to fill out.
226     * Otherwise, adds an answered value to the question.
227     *
228     * @param value a default value or an answered value of the question.
229     */
230    public void addValue(String value) {
231        synchronized (values) {
232            values.add(value);
233        }
234    }
235
236    /**
237     * Adds a default values to the question if the question is part of a form to fill out.
238     * Otherwise, adds an answered values to the question.
239     *
240     * @param newValues default values or an answered values of the question.
241     */
242    public void addValues(List<String> newValues) {
243        synchronized (values) {
244            values.addAll(newValues);
245        }
246    }
247
248    /**
249     * Removes all the values of the field.
250     */
251    protected void resetValues() {
252        synchronized (values) {
253            values.removeAll(new ArrayList<String>(values));
254        }
255    }
256
257    /**
258     * Adss an available options to the question that the user has in order to answer
259     * the question.
260     *
261     * @param option a new available option for the question.
262     */
263    public void addOption(Option option) {
264        synchronized (options) {
265            options.add(option);
266        }
267    }
268
269    public String toXML() {
270        StringBuilder buf = new StringBuilder();
271        buf.append("<field");
272        // Add attributes
273        if (getLabel() != null) {
274            buf.append(" label=\"").append(getLabel()).append("\"");
275        }
276        if (getVariable() != null) {
277            buf.append(" var=\"").append(getVariable()).append("\"");
278        }
279        if (getType() != null) {
280            buf.append(" type=\"").append(getType()).append("\"");
281        }
282        buf.append(">");
283        // Add elements
284        if (getDescription() != null) {
285            buf.append("<desc>").append(getDescription()).append("</desc>");
286        }
287        if (isRequired()) {
288            buf.append("<required/>");
289        }
290        // Loop through all the values and append them to the string buffer
291        for (Iterator<String> i = getValues(); i.hasNext();) {
292            buf.append("<value>").append(i.next()).append("</value>");
293        }
294        // Loop through all the values and append them to the string buffer
295        for (Iterator<Option> i = getOptions(); i.hasNext();) {
296            buf.append((i.next()).toXML());
297        }
298        buf.append("</field>");
299        return buf.toString();
300    }
301
302    @Override
303    public boolean equals(Object obj) {
304        if (obj == null)
305            return false;
306        if (obj == this)
307            return true;
308        if (!(obj instanceof FormField))
309            return false;
310
311        FormField other = (FormField) obj;
312
313        return toXML().equals(other.toXML());
314    }
315
316    @Override
317    public int hashCode() {
318        return toXML().hashCode();
319    }
320
321    /**
322     * Represents the available option of a given FormField.
323     *
324     * @author Gaston Dombiak
325     */
326    public static class Option {
327
328        private String label;
329        private String value;
330
331        public Option(String value) {
332            this.value = value;
333        }
334
335        public Option(String label, String value) {
336            this.label = label;
337            this.value = value;
338        }
339
340        /**
341         * Returns the label that represents the option.
342         *
343         * @return the label that represents the option.
344         */
345        public String getLabel() {
346            return label;
347        }
348
349        /**
350         * Returns the value of the option.
351         *
352         * @return the value of the option.
353         */
354        public String getValue() {
355            return value;
356        }
357
358        @Override
359        public String toString() {
360            return getLabel();
361        }
362
363        public String toXML() {
364            StringBuilder buf = new StringBuilder();
365            buf.append("<option");
366            // Add attribute
367            if (getLabel() != null) {
368                buf.append(" label=\"").append(getLabel()).append("\"");
369            }
370            buf.append(">");
371            // Add element
372            buf.append("<value>").append(StringUtils.escapeForXML(getValue())).append("</value>");
373
374            buf.append("</option>");
375            return buf.toString();
376        }
377
378        @Override
379        public boolean equals(Object obj) {
380            if (obj == null)
381                return false;
382            if (obj == this)
383                return true;
384            if (obj.getClass() != getClass())
385                return false;
386
387            Option other = (Option) obj;
388
389            if (!value.equals(other.value))
390                return false;
391
392            String thisLabel = label == null ? "" : label;
393            String otherLabel = other.label == null ? "" : other.label;
394
395            if (!thisLabel.equals(otherLabel))
396                return false;
397
398            return true;
399        }
400
401        @Override
402        public int hashCode() {
403            int result = 1;
404            result = 37 * result + value.hashCode();
405            result = 37 * result + (label == null ? 0 : label.hashCode());
406            return result;
407        }
408    }
409}
410