DataForm.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.packet;

  18. import org.jivesoftware.smack.packet.Element;
  19. import org.jivesoftware.smack.packet.Stanza;
  20. import org.jivesoftware.smack.packet.ExtensionElement;
  21. import org.jivesoftware.smack.util.XmlStringBuilder;
  22. import org.jivesoftware.smackx.xdata.FormField;

  23. import java.util.ArrayList;
  24. import java.util.Collections;
  25. import java.util.List;
  26. import java.util.Locale;

  27. /**
  28.  * Represents a form that could be use for gathering data as well as for reporting data
  29.  * returned from a search.
  30.  *
  31.  * @author Gaston Dombiak
  32.  */
  33. public class DataForm implements ExtensionElement {

  34.     public static final String NAMESPACE = "jabber:x:data";
  35.     public static final String ELEMENT = "x";

  36.     public enum Type {
  37.         /**
  38.          * This packet contains a form to fill out. Display it to the user (if your program can).
  39.          */
  40.         form,

  41.         /**
  42.          * The form is filled out, and this is the data that is being returned from the form.
  43.          */
  44.         submit,

  45.         /**
  46.          * The form was cancelled. Tell the asker that piece of information.
  47.          */
  48.         cancel,

  49.         /**
  50.          * Data results being returned from a search, or some other query.
  51.          */
  52.         result,
  53.         ;

  54.         public static Type fromString(String string) {
  55.             return Type.valueOf(string.toLowerCase(Locale.US));
  56.         }
  57.     }

  58.     private Type type;
  59.     private String title;
  60.     private List<String> instructions = new ArrayList<String>();
  61.     private ReportedData reportedData;
  62.     private final List<Item> items = new ArrayList<Item>();
  63.     private final List<FormField> fields = new ArrayList<FormField>();
  64.     private final List<Element> extensionElements = new ArrayList<Element>();
  65.    
  66.     public DataForm(Type type) {
  67.         this.type = type;
  68.     }
  69.    
  70.     /**
  71.      * Returns the meaning of the data within the context. The data could be part of a form
  72.      * to fill out, a form submission or data results.
  73.      *
  74.      * @return the form's type.
  75.      */
  76.     public Type getType() {
  77.         return type;
  78.     }
  79.    
  80.     /**
  81.      * Returns the description of the data. It is similar to the title on a web page or an X
  82.      * window.  You can put a <title/> on either a form to fill out, or a set of data results.
  83.      *
  84.      * @return description of the data.
  85.      */
  86.     public String getTitle() {
  87.         return title;
  88.     }

  89.     /**
  90.      * Returns a List of the list of instructions that explain how to fill out the form and
  91.      * what the form is about. The dataform could include multiple instructions since each
  92.      * instruction could not contain newlines characters. Join the instructions together in order
  93.      * to show them to the user.
  94.      *
  95.      * @return a List of the list of instructions that explain how to fill out the form.
  96.      */
  97.     public List<String> getInstructions() {
  98.         synchronized (instructions) {
  99.             return Collections.unmodifiableList(new ArrayList<String>(instructions));
  100.         }
  101.     }

  102.     /**
  103.      * Returns the fields that will be returned from a search.
  104.      *
  105.      * @return fields that will be returned from a search.
  106.      */
  107.     public ReportedData getReportedData() {
  108.         return reportedData;
  109.     }

  110.     /**
  111.      * Returns a List of the items returned from a search.
  112.      *
  113.      * @return a List of the items returned from a search.
  114.      */
  115.     public List<Item> getItems() {
  116.         synchronized (items) {
  117.             return Collections.unmodifiableList(new ArrayList<Item>(items));
  118.         }
  119.     }

  120.     /**
  121.      * Returns a List of the fields that are part of the form.
  122.      *
  123.      * @return a List of the fields that are part of the form.
  124.      */
  125.     public List<FormField> getFields() {
  126.         synchronized (fields) {
  127.             return Collections.unmodifiableList(new ArrayList<FormField>(fields));
  128.         }
  129.     }

  130.     /**
  131.      * Return the form field with the given variable name or null.
  132.      *
  133.      * @param variableName
  134.      * @return the form field or null.
  135.      * @since 4.1
  136.      */
  137.     public FormField getField(String variableName) {
  138.         synchronized (fields) {
  139.             for (FormField field : fields) {
  140.                 if (variableName.equals(field.getVariable())) {
  141.                     return field;
  142.                 }
  143.             }
  144.         }
  145.         return null;
  146.     }

  147.     public String getElementName() {
  148.         return ELEMENT;
  149.     }

  150.     public String getNamespace() {
  151.         return NAMESPACE;
  152.     }

  153.     /**
  154.      * Sets the description of the data. It is similar to the title on a web page or an X window.
  155.      * You can put a <title/> on either a form to fill out, or a set of data results.
  156.      *
  157.      * @param title description of the data.
  158.      */
  159.     public void setTitle(String title) {
  160.         this.title = title;
  161.     }

  162.     /**
  163.      * Sets the list of instructions that explain how to fill out the form and what the form is
  164.      * about. The dataform could include multiple instructions since each instruction could not
  165.      * contain newlines characters.
  166.      *
  167.      * @param instructions list of instructions that explain how to fill out the form.
  168.      */
  169.     public void setInstructions(List<String> instructions) {
  170.         this.instructions = instructions;
  171.     }

  172.     /**
  173.      * Sets the fields that will be returned from a search.
  174.      *
  175.      * @param reportedData the fields that will be returned from a search.
  176.      */
  177.     public void setReportedData(ReportedData reportedData) {
  178.         this.reportedData = reportedData;
  179.     }

  180.     /**
  181.      * Adds a new field as part of the form.
  182.      *
  183.      * @param field the field to add to the form.
  184.      */
  185.     public void addField(FormField field) {
  186.         String fieldVariableName = field.getVariable();
  187.         if (fieldVariableName != null && getField(fieldVariableName) != null) {
  188.             throw new IllegalArgumentException("This data form already contains a form field with the variable name '"
  189.                             + fieldVariableName + "'");
  190.         }
  191.         synchronized (fields) {
  192.             fields.add(field);
  193.         }
  194.     }
  195.    
  196.     /**
  197.      * Adds a new instruction to the list of instructions that explain how to fill out the form
  198.      * and what the form is about. The dataform could include multiple instructions since each
  199.      * instruction could not contain newlines characters.
  200.      *
  201.      * @param instruction the new instruction that explain how to fill out the form.
  202.      */
  203.     public void addInstruction(String instruction) {
  204.         synchronized (instructions) {
  205.             instructions.add(instruction);
  206.         }
  207.     }

  208.     /**
  209.      * Adds a new item returned from a search.
  210.      *
  211.      * @param item the item returned from a search.
  212.      */
  213.     public void addItem(Item item) {
  214.         synchronized (items) {
  215.             items.add(item);
  216.         }
  217.     }

  218.     public void addExtensionElement(Element element) {
  219.         extensionElements.add(element);
  220.     }

  221.     public List<Element> getExtensionElements() {
  222.         return Collections.unmodifiableList(extensionElements);
  223.     }

  224.     /**
  225.      * Returns the hidden FORM_TYPE field or null if this data form has none.
  226.      *
  227.      * @return the hidden FORM_TYPE field or null.
  228.      * @since 4.1
  229.      */
  230.     public FormField getHiddenFormTypeField() {
  231.         FormField field = getField(FormField.FORM_TYPE);
  232.         if (field != null && field.getType() == FormField.Type.hidden) {
  233.             return field;
  234.         }
  235.         return null;
  236.     }

  237.     /**
  238.      * Returns true if this DataForm has at least one FORM_TYPE field which is
  239.      * hidden. This method is used for sanity checks.
  240.      *
  241.      * @return true if there is at least one field which is hidden.
  242.      */
  243.     public boolean hasHiddenFormTypeField() {
  244.         return getHiddenFormTypeField() != null;
  245.     }

  246.     @Override
  247.     public XmlStringBuilder toXML() {
  248.         XmlStringBuilder buf = new XmlStringBuilder(this);
  249.         buf.attribute("type", getType());
  250.         buf.rightAngleBracket();

  251.         buf.optElement("title", getTitle());
  252.         for (String instruction : getInstructions()) {
  253.             buf.element("instructions", instruction);
  254.         }
  255.         // Append the list of fields returned from a search
  256.         if (getReportedData() != null) {
  257.             buf.append(getReportedData().toXML());
  258.         }
  259.         // Loop through all the items returned from a search and append them to the string buffer
  260.         for (Item item : getItems()) {
  261.             buf.append(item.toXML());
  262.         }
  263.         // Loop through all the form fields and append them to the string buffer
  264.         for (FormField field : getFields()) {
  265.             buf.append(field.toXML());
  266.         }
  267.         for (Element element : extensionElements) {
  268.             buf.append(element.toXML());
  269.         }
  270.         buf.closeElement(this);
  271.         return buf;
  272.     }

  273.     /**
  274.      *
  275.      * @param packet
  276.      * @return the DataForm or null
  277.      */
  278.     public static DataForm from(Stanza packet) {
  279.         return (DataForm) packet.getExtension(ELEMENT, NAMESPACE);
  280.     }

  281.     /**
  282.      *
  283.      * Represents the fields that will be returned from a search. This information is useful when
  284.      * you try to use the jabber:iq:search namespace to return dynamic form information.
  285.      *
  286.      * @author Gaston Dombiak
  287.      */
  288.     public static class ReportedData {
  289.         public static final String ELEMENT = "reported";

  290.         private List<FormField> fields = new ArrayList<FormField>();
  291.        
  292.         public ReportedData(List<FormField> fields) {
  293.             this.fields = fields;
  294.         }

  295.         /**
  296.          * Returns the fields returned from a search.
  297.          *
  298.          * @return the fields returned from a search.
  299.          */
  300.         public List<FormField> getFields() {
  301.             return Collections.unmodifiableList(new ArrayList<FormField>(fields));
  302.         }

  303.         public CharSequence toXML() {
  304.             XmlStringBuilder buf = new XmlStringBuilder();
  305.             buf.openElement(ELEMENT);
  306.             // Loop through all the form items and append them to the string buffer
  307.             for (FormField field : getFields()) {
  308.                 buf.append(field.toXML());
  309.             }
  310.             buf.closeElement(ELEMENT);
  311.             return buf;
  312.         }
  313.     }
  314.    
  315.     /**
  316.      *
  317.      * Represents items of reported data.
  318.      *
  319.      * @author Gaston Dombiak
  320.      */
  321.     public static class Item {
  322.         public static final String ELEMENT = "item";

  323.         private List<FormField> fields = new ArrayList<FormField>();
  324.        
  325.         public Item(List<FormField> fields) {
  326.             this.fields = fields;
  327.         }
  328.        
  329.         /**
  330.          * Returns the fields that define the data that goes with the item.
  331.          *
  332.          * @return the fields that define the data that goes with the item.
  333.          */
  334.         public List<FormField> getFields() {
  335.             return Collections.unmodifiableList(new ArrayList<FormField>(fields));
  336.         }
  337.        
  338.         public CharSequence toXML() {
  339.             XmlStringBuilder buf = new XmlStringBuilder();
  340.             buf.openElement(ELEMENT);
  341.             // Loop through all the form items and append them to the string buffer
  342.             for (FormField field : getFields()) {
  343.                 buf.append(field.toXML());
  344.             }
  345.             buf.closeElement(ELEMENT);
  346.             return buf;
  347.         }
  348.     }
  349. }