FormField.java

  1. /**
  2.  *
  3.  * Copyright 2003-2007 Jive Software.
  4.  *
  5.  * Licensed under the Apache License, Version 2.0 (the "License");
  6.  * you may not use this file except in compliance with the License.
  7.  * You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. package org.jivesoftware.smackx.xdata;

  18. import java.util.ArrayList;
  19. import java.util.Collections;
  20. import java.util.List;

  21. import org.jivesoftware.smack.packet.NamedElement;
  22. import org.jivesoftware.smack.util.XmlStringBuilder;
  23. import org.jivesoftware.smackx.xdatavalidation.packet.ValidateElement;

  24. /**
  25.  * Represents a field of a form. The field could be used to represent a question to complete,
  26.  * a completed question or a data returned from a search. The exact interpretation of the field
  27.  * depends on the context where the field is used.
  28.  *
  29.  * @author Gaston Dombiak
  30.  */
  31. public class FormField implements NamedElement {

  32.     public static final String ELEMENT = "field";

  33.     /**
  34.      * The constant String "FORM_TYPE"
  35.      */
  36.     public static final String FORM_TYPE = "FORM_TYPE";

  37.     /**
  38.      * Form Field Types as defined in XEP-4 § 3.3.
  39.      *
  40.      * @see <a href="http://xmpp.org/extensions/xep-0004.html#protocol-fieldtypes">XEP-4 § 3.3 Field Types</a>
  41.      */
  42.     public enum Type {

  43.         /**
  44.          * Boolean type. Can be 0 or 1, true or false, yes or no. Default value is 0.
  45.          * <p>
  46.          * Note that in XEP-4 this type is called 'boolean', but since that String is a restricted keyword in Java, it
  47.          * is named 'bool' in Smack.
  48.          * </p>
  49.          */
  50.         bool,

  51.         /**
  52.          * Fixed for putting in text to show sections, or just advertise your web site in the middle of the form
  53.          */
  54.         fixed,

  55.         /**
  56.          * Is not given to the user at all, but returned with the questionnaire
  57.          */
  58.         hidden,

  59.         /**
  60.          * multiple entries for JIDs
  61.          */
  62.         jid_multi,

  63.         /**
  64.          * Jabber ID - choosing a JID from your roster, and entering one based on the rules for a JID.
  65.          */
  66.         jid_single,

  67.         /**
  68.          * Given a list of choices, pick one or more.
  69.          */
  70.         list_multi,

  71.         /**
  72.          * Given a list of choices, pick one.
  73.          */
  74.         list_single,

  75.         /**
  76.          * Multiple lines of text entry.
  77.          */
  78.         text_multi,

  79.         /**
  80.          * Instead of showing the user what they typed, you show ***** to protect it.
  81.          */
  82.         text_private,

  83.         /**
  84.          * Single line or word of text.
  85.          */
  86.         text_single,
  87.         ;

  88.         @Override
  89.         public String toString() {
  90.             switch (this) {
  91.             case bool:
  92.                 return "boolean";
  93.             default:
  94.                 return this.name().replace('_', '-');
  95.             }
  96.         }

  97.         /**
  98.          * Get a form field type from the given string. If <code>string</code> is null, then null will be returned.
  99.          *
  100.          * @param string the string to transform or null.
  101.          * @return the type or null.
  102.          */
  103.         public static Type fromString(String string) {
  104.             if (string == null) {
  105.                 return null;
  106.             }
  107.             switch (string) {
  108.             case "boolean":
  109.                 return bool;
  110.             default:
  111.                 string = string.replace('-', '_');
  112.                 return Type.valueOf(string);
  113.             }
  114.         }
  115.     }

  116.     private final String variable;

  117.     private String description;
  118.     private boolean required = false;
  119.     private String label;
  120.     private Type type;
  121.     private final List<Option> options = new ArrayList<Option>();
  122.     private final List<String> values = new ArrayList<String>();
  123.     private ValidateElement validateElement;

  124.     /**
  125.      * Creates a new FormField with the variable name that uniquely identifies the field
  126.      * in the context of the form.
  127.      *
  128.      * @param variable the variable name of the question.
  129.      */
  130.     public FormField(String variable) {
  131.         this.variable = variable;
  132.     }

  133.     /**
  134.      * Creates a new FormField of type FIXED. The fields of type FIXED do not define a variable
  135.      * name.
  136.      */
  137.     public FormField() {
  138.         this(null);
  139.         this.type = Type.fixed;
  140.     }

  141.     /**
  142.      * Returns a description that provides extra clarification about the question. This information
  143.      * could be presented to the user either in tool-tip, help button, or as a section of text
  144.      * before the question.<p>
  145.      * <p/>
  146.      * If the question is of type FIXED then the description should remain empty.
  147.      *
  148.      * @return description that provides extra clarification about the question.
  149.      */
  150.     public String getDescription() {
  151.         return description;
  152.     }

  153.     /**
  154.      * Returns the label of the question which should give enough information to the user to
  155.      * fill out the form.
  156.      *
  157.      * @return label of the question.
  158.      */
  159.     public String getLabel() {
  160.         return label;
  161.     }

  162.     /**
  163.      * Returns a List of the available options that the user has in order to answer
  164.      * the question.
  165.      *
  166.      * @return List of the available options.
  167.      */
  168.     public List<Option> getOptions() {
  169.         synchronized (options) {
  170.             return Collections.unmodifiableList(new ArrayList<Option>(options));
  171.         }
  172.     }

  173.     /**
  174.      * Returns true if the question must be answered in order to complete the questionnaire.
  175.      *
  176.      * @return true if the question must be answered in order to complete the questionnaire.
  177.      */
  178.     public boolean isRequired() {
  179.         return required;
  180.     }

  181.     /**
  182.      * Returns an indicative of the format for the data to answer.
  183.      *
  184.      * @return format for the data to answer.
  185.      * @see Type
  186.      */
  187.     public Type getType() {
  188.         return type;
  189.     }

  190.     /**
  191.      * Returns a List of the default values of the question if the question is part
  192.      * of a form to fill out. Otherwise, returns a List of the answered values of
  193.      * the question.
  194.      *
  195.      * @return a List of the default values or answered values of the question.
  196.      */
  197.     public List<String> getValues() {
  198.         synchronized (values) {
  199.             return Collections.unmodifiableList(new ArrayList<String>(values));
  200.         }
  201.     }

  202.     /**
  203.      * Returns the variable name that the question is filling out.
  204.      * <p>
  205.      * According to XEP-4 § 3.2 the variable name (the 'var' attribute)
  206.      * "uniquely identifies the field in the context of the form" (if the field is not of type 'fixed', in which case
  207.      * the field "MAY possess a 'var' attribute")
  208.      * </p>
  209.      *
  210.      * @return the variable name of the question.
  211.      */
  212.     public String getVariable() {
  213.         return variable;
  214.     }

  215.     /**
  216.      * @return the validateElement
  217.      */
  218.     public ValidateElement getValidateElement() {
  219.         return validateElement;
  220.     }

  221.     /**
  222.      * Sets a description that provides extra clarification about the question. This information
  223.      * could be presented to the user either in tool-tip, help button, or as a section of text
  224.      * before the question.<p>
  225.      * <p/>
  226.      * If the question is of type FIXED then the description should remain empty.
  227.      *
  228.      * @param description provides extra clarification about the question.
  229.      */
  230.     public void setDescription(String description) {
  231.         this.description = description;
  232.     }

  233.     /**
  234.      * Sets the label of the question which should give enough information to the user to
  235.      * fill out the form.
  236.      *
  237.      * @param label the label of the question.
  238.      */
  239.     public void setLabel(String label) {
  240.         this.label = label;
  241.     }

  242.     /**
  243.      * Sets if the question must be answered in order to complete the questionnaire.
  244.      *
  245.      * @param required if the question must be answered in order to complete the questionnaire.
  246.      */
  247.     public void setRequired(boolean required) {
  248.         this.required = required;
  249.     }

  250.     /**
  251.      * @param validateElement the validateElement to set
  252.      */
  253.     public void setValidateElement(ValidateElement validateElement) {
  254.         validateElement.checkConsistency(this);
  255.         this.validateElement = validateElement;
  256.     }

  257.     /**
  258.      * Sets an indicative of the format for the data to answer.
  259.      * <p>
  260.      * This method will throw an IllegalArgumentException if type is 'fixed'. To create FormFields of type 'fixed' use
  261.      * {@link #FormField()} instead.
  262.      * </p>
  263.      *
  264.      * @param type an indicative of the format for the data to answer.
  265.      * @see Type
  266.      * @throws IllegalArgumentException if type is 'fixed'.
  267.      */
  268.     public void setType(Type type) {
  269.         if (type == Type.fixed) {
  270.             throw new IllegalArgumentException("Can not set type to fixed, use FormField constructor without arguments instead.");
  271.         }
  272.         this.type = type;
  273.     }

  274.     /**
  275.      * Adds a default value to the question if the question is part of a form to fill out.
  276.      * Otherwise, adds an answered value to the question.
  277.      *
  278.      * @param value a default value or an answered value of the question.
  279.      */
  280.     public void addValue(String value) {
  281.         synchronized (values) {
  282.             values.add(value);
  283.         }
  284.     }

  285.     /**
  286.      * Adds a default values to the question if the question is part of a form to fill out.
  287.      * Otherwise, adds an answered values to the question.
  288.      *
  289.      * @param newValues default values or an answered values of the question.
  290.      */
  291.     public void addValues(List<String> newValues) {
  292.         synchronized (values) {
  293.             values.addAll(newValues);
  294.         }
  295.     }

  296.     /**
  297.      * Removes all the values of the field.
  298.      */
  299.     protected void resetValues() {
  300.         synchronized (values) {
  301.             values.removeAll(new ArrayList<String>(values));
  302.         }
  303.     }

  304.     /**
  305.      * Adss an available options to the question that the user has in order to answer
  306.      * the question.
  307.      *
  308.      * @param option a new available option for the question.
  309.      */
  310.     public void addOption(Option option) {
  311.         synchronized (options) {
  312.             options.add(option);
  313.         }
  314.     }

  315.     @Override
  316.     public String getElementName() {
  317.         return ELEMENT;
  318.     }

  319.     public XmlStringBuilder toXML() {
  320.         XmlStringBuilder buf = new XmlStringBuilder(this);
  321.         // Add attributes
  322.         buf.optAttribute("label", getLabel());
  323.         buf.optAttribute("var", getVariable());
  324.         buf.optAttribute("type", getType());
  325.         buf.rightAngleBracket();
  326.         // Add elements
  327.         buf.optElement("desc", getDescription());
  328.         buf.condEmptyElement(isRequired(), "required");
  329.         // Loop through all the values and append them to the string buffer
  330.         for (String value : getValues()) {
  331.             buf.element("value", value);
  332.         }
  333.         // Loop through all the values and append them to the string buffer
  334.         for (Option option : getOptions()) {
  335.             buf.append(option.toXML());
  336.         }
  337.         buf.optElement(validateElement);
  338.         buf.closeElement(this);
  339.         return buf;
  340.     }

  341.     @Override
  342.     public boolean equals(Object obj) {
  343.         if (obj == null)
  344.             return false;
  345.         if (obj == this)
  346.             return true;
  347.         if (!(obj instanceof FormField))
  348.             return false;

  349.         FormField other = (FormField) obj;

  350.         return toXML().equals(other.toXML());
  351.     }

  352.     @Override
  353.     public int hashCode() {
  354.         return toXML().hashCode();
  355.     }

  356.     /**
  357.      * Represents the available option of a given FormField.
  358.      *
  359.      * @author Gaston Dombiak
  360.      */
  361.     public static class Option implements NamedElement {

  362.         public static final String ELEMENT = "option";

  363.         private final String value;
  364.         private String label;

  365.         public Option(String value) {
  366.             this.value = value;
  367.         }

  368.         public Option(String label, String value) {
  369.             this.label = label;
  370.             this.value = value;
  371.         }

  372.         /**
  373.          * Returns the label that represents the option.
  374.          *
  375.          * @return the label that represents the option.
  376.          */
  377.         public String getLabel() {
  378.             return label;
  379.         }

  380.         /**
  381.          * Returns the value of the option.
  382.          *
  383.          * @return the value of the option.
  384.          */
  385.         public String getValue() {
  386.             return value;
  387.         }

  388.         @Override
  389.         public String toString() {
  390.             return getLabel();
  391.         }

  392.         @Override
  393.         public String getElementName() {
  394.             return ELEMENT;
  395.         }

  396.         public XmlStringBuilder toXML() {
  397.             XmlStringBuilder xml = new XmlStringBuilder(this);
  398.             // Add attribute
  399.             xml.optAttribute("label", getLabel());
  400.             xml.rightAngleBracket();

  401.             // Add element
  402.             xml.element("value", getValue());

  403.             xml.closeElement(this);
  404.             return xml;
  405.         }

  406.         @Override
  407.         public boolean equals(Object obj) {
  408.             if (obj == null)
  409.                 return false;
  410.             if (obj == this)
  411.                 return true;
  412.             if (obj.getClass() != getClass())
  413.                 return false;

  414.             Option other = (Option) obj;

  415.             if (!value.equals(other.value))
  416.                 return false;

  417.             String thisLabel = label == null ? "" : label;
  418.             String otherLabel = other.label == null ? "" : other.label;

  419.             if (!thisLabel.equals(otherLabel))
  420.                 return false;

  421.             return true;
  422.         }

  423.         @Override
  424.         public int hashCode() {
  425.             int result = 1;
  426.             result = 37 * result + value.hashCode();
  427.             result = 37 * result + (label == null ? 0 : label.hashCode());
  428.             return result;
  429.         }
  430.     }
  431. }