001: /**
002: * Copyright (C) 2004-2007 Jive Software. All rights reserved.
003: *
004: * This software is published under the terms of the GNU Public License (GPL),
005: * a copy of which is included in this distribution.
006: */package org.xmpp.forms;
007:
008: import org.dom4j.Element;
009:
010: import java.util.ArrayList;
011: import java.util.Iterator;
012: import java.util.List;
013:
014: /**
015: * Represents a field of a form. The field could be used to represent a question to complete,
016: * a completed question or a data returned from a search. The exact interpretation of the field
017: * depends on the context where the field is used.
018: *
019: * @author Gaston Dombiak
020: */
021: public class FormField {
022:
023: private Element element;
024:
025: FormField(Element element) {
026: this .element = element;
027: }
028:
029: /**
030: * Adds a default value to the question if the question is part of a form to fill out.
031: * Otherwise, adds an answered value to the question.
032: *
033: * @param value a default value or an answered value of the question.
034: */
035: public void addValue(Object value) {
036: element.addElement("value").setText(DataForm.encode(value));
037: }
038:
039: /**
040: * Removes all the values of the field.
041: */
042: public void clearValues() {
043: for (Iterator it = element.elementIterator("value"); it
044: .hasNext();) {
045: it.next();
046: it.remove();
047: }
048: }
049:
050: /**
051: * Adds an available option to the question that the user has in order to answer
052: * the question.
053: *
054: * @param label a label that represents the option.
055: * @param value the value of the option.
056: */
057: public void addOption(String label, String value) {
058: Element option = element.addElement("option");
059: option.addAttribute("label", label);
060: option.addElement("value").setText(value);
061: }
062:
063: /**
064: * Returns the available options to answer for this question. The returned options cannot
065: * be modified but they will be updated if the underlying DOM object gets updated.
066: *
067: * @return the available options to answer for this question.
068: */
069: public List<Option> getOptions() {
070: List<Option> answer = new ArrayList<Option>();
071: for (Iterator it = element.elementIterator("option"); it
072: .hasNext();) {
073: answer.add(new Option((Element) it.next()));
074: }
075: return answer;
076: }
077:
078: /**
079: * Sets an indicative of the format for the data to answer. Valid formats are:
080: * <p/>
081: * <ul>
082: * <li>text-single -> single line or word of text
083: * <li>text-private -> instead of showing the user what they typed, you show ***** to
084: * protect it
085: * <li>text-multi -> multiple lines of text entry
086: * <li>list-single -> given a list of choices, pick one
087: * <li>list-multi -> given a list of choices, pick one or more
088: * <li>boolean -> 0 or 1, true or false, yes or no. Default value is 0
089: * <li>fixed -> fixed for putting in text to show sections, or just advertise your web
090: * site in the middle of the form
091: * <li>hidden -> is not given to the user at all, but returned with the questionnaire
092: * <li>jid-single -> Jabber ID - choosing a JID from your roster, and entering one based
093: * on the rules for a JID.
094: * <li>jid-multi -> multiple entries for JIDs
095: * </ul>
096: *
097: * @param type an indicative of the format for the data to answer.
098: */
099: public void setType(Type type) {
100: element.addAttribute("type", type == null ? null : type
101: .toXMPP());
102: }
103:
104: /**
105: * Sets the attribute that uniquely identifies the field in the context of the form. If the
106: * field is of type "fixed" then the variable is optional.
107: *
108: * @param var the unique identifier of the field in the context of the form.
109: */
110: public void setVariable(String var) {
111: element.addAttribute("var", var);
112: }
113:
114: /**
115: * Sets the label of the question which should give enough information to the user to
116: * fill out the form.
117: *
118: * @param label the label of the question.
119: */
120: public void setLabel(String label) {
121: element.addAttribute("label", label);
122: }
123:
124: /**
125: * Sets if the question must be answered in order to complete the questionnaire.
126: *
127: * @param required if the question must be answered in order to complete the questionnaire.
128: */
129: public void setRequired(boolean required) {
130: // Remove an existing desc element.
131: if (element.element("required") != null) {
132: element.remove(element.element("required"));
133: }
134: if (required) {
135: element.addElement("required");
136: }
137: }
138:
139: /**
140: * Sets a description that provides extra clarification about the question. This information
141: * could be presented to the user either in tool-tip, help button, or as a section of text
142: * before the question.<p>
143: * <p/>
144: * If the question is of type FIXED then the description should remain empty.
145: *
146: * @param description provides extra clarification about the question.
147: */
148: public void setDescription(String description) {
149: // Remove an existing desc element.
150: if (element.element("desc") != null) {
151: element.remove(element.element("desc"));
152: }
153: element.addElement("desc").setText(description);
154: }
155:
156: /**
157: * Returns true if the question must be answered in order to complete the questionnaire.
158: *
159: * @return true if the question must be answered in order to complete the questionnaire.
160: */
161: public boolean isRequired() {
162: return element.element("required") != null;
163: }
164:
165: /**
166: * Returns the variable name that the question is filling out.
167: *
168: * @return the variable name of the question.
169: */
170: public String getVariable() {
171: return element.attributeValue("var");
172: }
173:
174: /**
175: * Returns an Iterator for the default values of the question if the question is part
176: * of a form to fill out. Otherwise, returns an Iterator for the answered values of
177: * the question.
178: *
179: * @return an Iterator for the default values or answered values of the question.
180: */
181: public List<String> getValues() {
182: List<String> answer = new ArrayList<String>();
183: for (Iterator it = element.elementIterator("value"); it
184: .hasNext();) {
185: answer.add(((Element) it.next()).getTextTrim());
186: }
187: return answer;
188: }
189:
190: /**
191: * Returns an indicative of the format for the data to answer. Valid formats are:
192: * <p/>
193: * <ul>
194: * <li>text-single -> single line or word of text
195: * <li>text-private -> instead of showing the user what they typed, you show ***** to
196: * protect it
197: * <li>text-multi -> multiple lines of text entry
198: * <li>list-single -> given a list of choices, pick one
199: * <li>list-multi -> given a list of choices, pick one or more
200: * <li>boolean -> 0 or 1, true or false, yes or no. Default value is 0
201: * <li>fixed -> fixed for putting in text to show sections, or just advertise your web
202: * site in the middle of the form
203: * <li>hidden -> is not given to the user at all, but returned with the questionnaire
204: * <li>jid-single -> Jabber ID - choosing a JID from your roster, and entering one based
205: * on the rules for a JID.
206: * <li>jid-multi -> multiple entries for JIDs
207: * </ul>
208: *
209: * @return format for the data to answer.
210: */
211: public Type getType() {
212: String type = element.attributeValue("type");
213: if (type != null) {
214: Type.fromXMPP(type);
215: }
216: return null;
217: }
218:
219: /**
220: * Returns the label of the question which should give enough information to the user to
221: * fill out the form.
222: *
223: * @return label of the question.
224: */
225: public String getLabel() {
226: return element.attributeValue("label");
227: }
228:
229: /**
230: * Returns a description that provides extra clarification about the question. This information
231: * could be presented to the user either in tool-tip, help button, or as a section of text
232: * before the question.<p>
233: * <p/>
234: * If the question is of type FIXED then the description should remain empty.
235: *
236: * @return description that provides extra clarification about the question.
237: */
238: public String getDescription() {
239: return element.elementTextTrim("desc");
240: }
241:
242: /**
243: * Represents the available option of a given FormField.
244: *
245: * @author Gaston Dombiak
246: */
247: public static class Option {
248: private Element element;
249:
250: private Option(Element element) {
251: this .element = element;
252: }
253:
254: /**
255: * Returns the label that represents the option.
256: *
257: * @return the label that represents the option.
258: */
259: public String getLabel() {
260: return element.attributeValue("label");
261: }
262:
263: /**
264: * Returns the value of the option.
265: *
266: * @return the value of the option.
267: */
268: public String getValue() {
269: return element.elementTextTrim("value");
270: }
271: }
272:
273: /**
274: * Type-safe enumeration to represent the field type of the Data forms.<p>
275: *
276: * Implementation note: XMPP error conditions use "-" characters in
277: * their names such as "jid-multi". Because "-" characters are not valid
278: * identifier parts in Java, they have been converted to "_" characters in
279: * the enumeration names, such as <tt>jid_multi</tt>. The {@link #toXMPP()} and
280: * {@link #fromXMPP(String)} methods can be used to convert between the
281: * enumertation values and Type code strings.
282: */
283: public enum Type {
284: /**
285: * The field enables an entity to gather or provide an either-or choice between two
286: * options. The allowable values are 1 for yes/true/assent and 0 for no/false/decline.
287: * The default value is 0.
288: */
289: boolean_type("boolean"),
290:
291: /**
292: * The field is intended for data description (e.g., human-readable text such as
293: * "section" headers) rather than data gathering or provision. The <value/> child
294: * SHOULD NOT contain newlines (the \n and \r characters); instead an application
295: * SHOULD generate multiple fixed fields, each with one <value/> child.
296: */
297: fixed("fixed"),
298:
299: /**
300: * The field is not shown to the entity providing information, but instead is
301: * returned with the form.
302: */
303: hidden("hidden"),
304:
305: /**
306: * The field enables an entity to gather or provide multiple Jabber IDs.
307: */
308: jid_multi("jid-multi"),
309:
310: /**
311: * The field enables an entity to gather or provide multiple Jabber IDs.
312: */
313: jid_single("jid-single"),
314:
315: /**
316: * The field enables an entity to gather or provide one or more options from
317: * among many.
318: */
319: list_multi("list-multi"),
320:
321: /**
322: * The field enables an entity to gather or provide one option from among many.
323: */
324: list_single("list-single"),
325:
326: /**
327: * The field enables an entity to gather or provide multiple lines of text.
328: */
329: text_multi("text-multi"),
330:
331: /**
332: * The field enables an entity to gather or provide a single line or word of text,
333: * which shall be obscured in an interface (e.g., *****).
334: */
335: text_private("text-private"),
336:
337: /**
338: * The field enables an entity to gather or provide a single line or word of text,
339: * which may be shown in an interface. This field type is the default and MUST be
340: * assumed if an entity receives a field type it does not understand.
341: */
342: text_single("text-single");
343:
344: /**
345: * Converts a String value into its Type representation.
346: *
347: * @param type the String value.
348: * @return the type corresponding to the String.
349: */
350: public static Type fromXMPP(String type) {
351: if (type == null) {
352: throw new NullPointerException();
353: }
354: type = type.toLowerCase();
355: if (boolean_type.toXMPP().equals(type)) {
356: return boolean_type;
357: } else if (fixed.toXMPP().equals(type)) {
358: return fixed;
359: } else if (hidden.toXMPP().equals(type)) {
360: return hidden;
361: } else if (jid_multi.toXMPP().equals(type)) {
362: return jid_multi;
363: } else if (jid_single.toXMPP().equals(type)) {
364: return jid_single;
365: } else if (list_multi.toXMPP().equals(type)) {
366: return list_multi;
367: } else if (list_single.toXMPP().equals(type)) {
368: return list_single;
369: } else if (text_multi.toXMPP().equals(type)) {
370: return text_multi;
371: } else if (text_private.toXMPP().equals(type)) {
372: return text_private;
373: } else if (text_single.toXMPP().equals(type)) {
374: return text_single;
375: } else {
376: throw new IllegalArgumentException("Type invalid:"
377: + type);
378: }
379: }
380:
381: private String value;
382:
383: private Type(String value) {
384: this .value = value;
385: }
386:
387: /**
388: * Returns the Field Type as a valid Field Type code string.
389: *
390: * @return the Field Type value.
391: */
392: public String toXMPP() {
393: return value;
394: }
395:
396: }
397: }
|