001/**
002 *
003 * Copyright 2003-2007 Jive Software.
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.jivesoftware.smackx.xdata;
019
020import org.jivesoftware.smack.util.XmlStringBuilder;
021
022import java.util.ArrayList;
023import java.util.Collections;
024import java.util.List;
025
026/**
027 * Represents a field of a form. The field could be used to represent a question to complete,
028 * a completed question or a data returned from a search. The exact interpretation of the field
029 * depends on the context where the field is used.
030 *
031 * @author Gaston Dombiak
032 */
033public class FormField {
034
035    public static final String ELEMENT = "field";
036
037    public static final String TYPE_BOOLEAN = "boolean";
038    public static final String TYPE_FIXED = "fixed";
039    public static final String TYPE_HIDDEN = "hidden";
040    public static final String TYPE_JID_MULTI = "jid-multi";
041    public static final String TYPE_JID_SINGLE = "jid-single";
042    public static final String TYPE_LIST_MULTI = "list-multi";
043    public static final String TYPE_LIST_SINGLE = "list-single";
044    public static final String TYPE_TEXT_MULTI = "text-multi";
045    public static final String TYPE_TEXT_PRIVATE = "text-private";
046    public static final String TYPE_TEXT_SINGLE = "text-single";
047
048    private String description;
049    private boolean required = false;
050    private String label;
051    private String variable;
052    private String type;
053    private final List<Option> options = new ArrayList<Option>();
054    private final List<String> values = new ArrayList<String>();
055
056    /**
057     * Creates a new FormField with the variable name that uniquely identifies the field
058     * in the context of the form.
059     *
060     * @param variable the variable name of the question.
061     */
062    public FormField(String variable) {
063        this.variable = variable;
064    }
065
066    /**
067     * Creates a new FormField of type FIXED. The fields of type FIXED do not define a variable
068     * name.
069     */
070    public FormField() {
071        this.type = FormField.TYPE_FIXED;
072    }
073
074    /**
075     * Returns a description that provides extra clarification about the question. This information
076     * could be presented to the user either in tool-tip, help button, or as a section of text
077     * before the question.<p>
078     * <p/>
079     * If the question is of type FIXED then the description should remain empty.
080     *
081     * @return description that provides extra clarification about the question.
082     */
083    public String getDescription() {
084        return description;
085    }
086
087    /**
088     * Returns the label of the question which should give enough information to the user to
089     * fill out the form.
090     *
091     * @return label of the question.
092     */
093    public String getLabel() {
094        return label;
095    }
096
097    /**
098     * Returns a List of the available options that the user has in order to answer
099     * the question.
100     *
101     * @return List of the available options.
102     */
103    public List<Option> getOptions() {
104        synchronized (options) {
105            return Collections.unmodifiableList(new ArrayList<Option>(options));
106        }
107    }
108
109    /**
110     * Returns true if the question must be answered in order to complete the questionnaire.
111     *
112     * @return true if the question must be answered in order to complete the questionnaire.
113     */
114    public boolean isRequired() {
115        return required;
116    }
117
118    /**
119     * Returns an indicative of the format for the data to answer. Valid formats are:
120     * <p/>
121     * <ul>
122     * <li>text-single -> single line or word of text
123     * <li>text-private -> instead of showing the user what they typed, you show ***** to
124     * protect it
125     * <li>text-multi -> multiple lines of text entry
126     * <li>list-single -> given a list of choices, pick one
127     * <li>list-multi -> given a list of choices, pick one or more
128     * <li>boolean -> 0 or 1, true or false, yes or no. Default value is 0
129     * <li>fixed -> fixed for putting in text to show sections, or just advertise your web
130     * site in the middle of the form
131     * <li>hidden -> is not given to the user at all, but returned with the questionnaire
132     * <li>jid-single -> Jabber ID - choosing a JID from your roster, and entering one based
133     * on the rules for a JID.
134     * <li>jid-multi -> multiple entries for JIDs
135     * </ul>
136     *
137     * @return format for the data to answer.
138     */
139    public String getType() {
140        return type;
141    }
142
143    /**
144     * Returns a List of the default values of the question if the question is part
145     * of a form to fill out. Otherwise, returns a List of the answered values of
146     * the question.
147     *
148     * @return a List of the default values or answered values of the question.
149     */
150    public List<String> getValues() {
151        synchronized (values) {
152            return Collections.unmodifiableList(new ArrayList<String>(values));
153        }
154    }
155
156    /**
157     * Returns the variable name that the question is filling out.
158     *
159     * @return the variable name of the question.
160     */
161    public String getVariable() {
162        return variable;
163    }
164
165    /**
166     * Sets a description that provides extra clarification about the question. This information
167     * could be presented to the user either in tool-tip, help button, or as a section of text
168     * before the question.<p>
169     * <p/>
170     * If the question is of type FIXED then the description should remain empty.
171     *
172     * @param description provides extra clarification about the question.
173     */
174    public void setDescription(String description) {
175        this.description = description;
176    }
177
178    /**
179     * Sets the label of the question which should give enough information to the user to
180     * fill out the form.
181     *
182     * @param label the label of the question.
183     */
184    public void setLabel(String label) {
185        this.label = label;
186    }
187
188    /**
189     * Sets if the question must be answered in order to complete the questionnaire.
190     *
191     * @param required if the question must be answered in order to complete the questionnaire.
192     */
193    public void setRequired(boolean required) {
194        this.required = required;
195    }
196
197    /**
198     * Sets an indicative of the format for the data to answer. Valid formats are:
199     * <p/>
200     * <ul>
201     * <li>text-single -> single line or word of text
202     * <li>text-private -> instead of showing the user what they typed, you show ***** to
203     * protect it
204     * <li>text-multi -> multiple lines of text entry
205     * <li>list-single -> given a list of choices, pick one
206     * <li>list-multi -> given a list of choices, pick one or more
207     * <li>boolean -> 0 or 1, true or false, yes or no. Default value is 0
208     * <li>fixed -> fixed for putting in text to show sections, or just advertise your web
209     * site in the middle of the form
210     * <li>hidden -> is not given to the user at all, but returned with the questionnaire
211     * <li>jid-single -> Jabber ID - choosing a JID from your roster, and entering one based
212     * on the rules for a JID.
213     * <li>jid-multi -> multiple entries for JIDs
214     * </ul>
215     *
216     * @param type an indicative of the format for the data to answer.
217     */
218    public void setType(String type) {
219        this.type = type;
220    }
221
222    /**
223     * Adds a default value to the question if the question is part of a form to fill out.
224     * Otherwise, adds an answered value to the question.
225     *
226     * @param value a default value or an answered value of the question.
227     */
228    public void addValue(String value) {
229        synchronized (values) {
230            values.add(value);
231        }
232    }
233
234    /**
235     * Adds a default values to the question if the question is part of a form to fill out.
236     * Otherwise, adds an answered values to the question.
237     *
238     * @param newValues default values or an answered values of the question.
239     */
240    public void addValues(List<String> newValues) {
241        synchronized (values) {
242            values.addAll(newValues);
243        }
244    }
245
246    /**
247     * Removes all the values of the field.
248     */
249    protected void resetValues() {
250        synchronized (values) {
251            values.removeAll(new ArrayList<String>(values));
252        }
253    }
254
255    /**
256     * Adss an available options to the question that the user has in order to answer
257     * the question.
258     *
259     * @param option a new available option for the question.
260     */
261    public void addOption(Option option) {
262        synchronized (options) {
263            options.add(option);
264        }
265    }
266
267    public XmlStringBuilder toXML() {
268        XmlStringBuilder buf = new XmlStringBuilder();
269        buf.halfOpenElement(ELEMENT);
270        // Add attributes
271        buf.optAttribute("label", getLabel());
272        buf.optAttribute("var", getVariable());
273        buf.optAttribute("type", getType());
274        buf.rightAngelBracket();
275        // Add elements
276        buf.optElement("desc", getDescription());
277        buf.condEmptyElement(isRequired(), "required");
278        // Loop through all the values and append them to the string buffer
279        for (String value : getValues()) {
280            buf.element("value", value);
281        }
282        // Loop through all the values and append them to the string buffer
283        for (Option option : getOptions()) {
284            buf.append(option.toXML());
285        }
286        buf.closeElement(ELEMENT);
287        return buf;
288    }
289
290    @Override
291    public boolean equals(Object obj) {
292        if (obj == null)
293            return false;
294        if (obj == this)
295            return true;
296        if (!(obj instanceof FormField))
297            return false;
298
299        FormField other = (FormField) obj;
300
301        return toXML().equals(other.toXML());
302    }
303
304    @Override
305    public int hashCode() {
306        return toXML().hashCode();
307    }
308
309    /**
310     * Represents the available option of a given FormField.
311     *
312     * @author Gaston Dombiak
313     */
314    public static class Option {
315
316        public static final String ELEMENT = "option";
317
318        private final String value;
319        private String label;
320
321        public Option(String value) {
322            this.value = value;
323        }
324
325        public Option(String label, String value) {
326            this.label = label;
327            this.value = value;
328        }
329
330        /**
331         * Returns the label that represents the option.
332         *
333         * @return the label that represents the option.
334         */
335        public String getLabel() {
336            return label;
337        }
338
339        /**
340         * Returns the value of the option.
341         *
342         * @return the value of the option.
343         */
344        public String getValue() {
345            return value;
346        }
347
348        @Override
349        public String toString() {
350            return getLabel();
351        }
352
353        public XmlStringBuilder toXML() {
354            XmlStringBuilder xml = new XmlStringBuilder();
355            xml.halfOpenElement(ELEMENT);
356            // Add attribute
357            xml.optAttribute("label", getLabel());
358            xml.rightAngelBracket();
359
360            // Add element
361            xml.element("value", getValue());
362
363            xml.closeElement(ELEMENT);
364            return xml;
365        }
366
367        @Override
368        public boolean equals(Object obj) {
369            if (obj == null)
370                return false;
371            if (obj == this)
372                return true;
373            if (obj.getClass() != getClass())
374                return false;
375
376            Option other = (Option) obj;
377
378            if (!value.equals(other.value))
379                return false;
380
381            String thisLabel = label == null ? "" : label;
382            String otherLabel = other.label == null ? "" : other.label;
383
384            if (!thisLabel.equals(otherLabel))
385                return false;
386
387            return true;
388        }
389
390        @Override
391        public int hashCode() {
392            int result = 1;
393            result = 37 * result + value.hashCode();
394            result = 37 * result + (label == null ? 0 : label.hashCode());
395            return result;
396        }
397    }
398}