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.packet;
019
020import org.jivesoftware.smack.packet.PacketExtension;
021import org.jivesoftware.smack.util.XmlStringBuilder;
022import org.jivesoftware.smackx.xdata.FormField;
023
024import java.util.ArrayList;
025import java.util.Collections;
026import java.util.List;
027
028/**
029 * Represents a form that could be use for gathering data as well as for reporting data
030 * returned from a search.
031 *
032 * @author Gaston Dombiak
033 */
034public class DataForm implements PacketExtension {
035
036    public static final String NAMESPACE = "jabber:x:data";
037    public static final String ELEMENT = "x";
038
039    private String type;
040    private String title;
041    private List<String> instructions = new ArrayList<String>();
042    private ReportedData reportedData;
043    private final List<Item> items = new ArrayList<Item>();
044    private final List<FormField> fields = new ArrayList<FormField>();
045    
046    public DataForm(String type) {
047        this.type = type;
048    }
049    
050    /**
051     * Returns the meaning of the data within the context. The data could be part of a form
052     * to fill out, a form submission or data results.<p>
053     * 
054     * Possible form types are:
055     * <ul>
056     *  <li>form -> This packet contains a form to fill out. Display it to the user (if your 
057     * program can).</li>
058     *  <li>submit -> The form is filled out, and this is the data that is being returned from 
059     * the form.</li>
060     *  <li>cancel -> The form was cancelled. Tell the asker that piece of information.</li>
061     *  <li>result -> Data results being returned from a search, or some other query.</li>
062     * </ul>
063     * 
064     * @return the form's type.
065     */
066    public String getType() {
067        return type; 
068    }
069    
070    /**
071     * Returns the description of the data. It is similar to the title on a web page or an X 
072     * window.  You can put a <title/> on either a form to fill out, or a set of data results.
073     * 
074     * @return description of the data.
075     */
076    public String getTitle() {
077        return title;
078    }
079
080    /**
081     * Returns a List of the list of instructions that explain how to fill out the form and 
082     * what the form is about. The dataform could include multiple instructions since each 
083     * instruction could not contain newlines characters. Join the instructions together in order 
084     * to show them to the user.
085     * 
086     * @return a List of the list of instructions that explain how to fill out the form.
087     */
088    public List<String> getInstructions() {
089        synchronized (instructions) {
090            return Collections.unmodifiableList(new ArrayList<String>(instructions));
091        }
092    }
093
094    /**
095     * Returns the fields that will be returned from a search.
096     * 
097     * @return fields that will be returned from a search.
098     */
099    public ReportedData getReportedData() {
100        return reportedData;
101    }
102
103    /**
104     * Returns a List of the items returned from a search.
105     *
106     * @return a List of the items returned from a search.
107     */
108    public List<Item> getItems() {
109        synchronized (items) {
110            return Collections.unmodifiableList(new ArrayList<Item>(items));
111        }
112    }
113
114    /**
115     * Returns a List of the fields that are part of the form.
116     *
117     * @return a List of the fields that are part of the form.
118     */
119    public List<FormField> getFields() {
120        synchronized (fields) {
121            return Collections.unmodifiableList(new ArrayList<FormField>(fields));
122        }
123    }
124
125    public String getElementName() {
126        return ELEMENT;
127    }
128
129    public String getNamespace() {
130        return NAMESPACE;
131    }
132
133    /**
134     * Sets the description of the data. It is similar to the title on a web page or an X window.
135     * You can put a <title/> on either a form to fill out, or a set of data results.
136     * 
137     * @param title description of the data.
138     */
139    public void setTitle(String title) {
140        this.title = title;
141    }
142
143    /**
144     * Sets the list of instructions that explain how to fill out the form and what the form is 
145     * about. The dataform could include multiple instructions since each instruction could not 
146     * contain newlines characters. 
147     * 
148     * @param instructions list of instructions that explain how to fill out the form.
149     */
150    public void setInstructions(List<String> instructions) {
151        this.instructions = instructions;
152    }
153
154    /**
155     * Sets the fields that will be returned from a search.
156     * 
157     * @param reportedData the fields that will be returned from a search.
158     */
159    public void setReportedData(ReportedData reportedData) {
160        this.reportedData = reportedData;
161    }
162
163    /**
164     * Adds a new field as part of the form.
165     * 
166     * @param field the field to add to the form.
167     */
168    public void addField(FormField field) {
169        synchronized (fields) {
170            fields.add(field);
171        }
172    }
173    
174    /**
175     * Adds a new instruction to the list of instructions that explain how to fill out the form 
176     * and what the form is about. The dataform could include multiple instructions since each 
177     * instruction could not contain newlines characters. 
178     * 
179     * @param instruction the new instruction that explain how to fill out the form.
180     */
181    public void addInstruction(String instruction) {
182        synchronized (instructions) {
183            instructions.add(instruction);
184        }
185    }
186
187    /**
188     * Adds a new item returned from a search.
189     * 
190     * @param item the item returned from a search.
191     */
192    public void addItem(Item item) {
193        synchronized (items) {
194            items.add(item);
195        }
196    }
197
198    /**
199     * Returns true if this DataForm has at least one FORM_TYPE field which is
200     * hidden. This method is used for sanity checks.
201     *
202     * @return true if there is at least one field which is hidden.
203     */
204    public boolean hasHiddenFormTypeField() {
205        boolean found = false;
206        for (FormField f : fields) {
207            if (f.getVariable().equals("FORM_TYPE") && f.getType() != null && f.getType().equals("hidden"))
208                found = true;
209        }
210        return found;
211    }
212
213    @Override
214    public XmlStringBuilder toXML() {
215        XmlStringBuilder buf = new XmlStringBuilder(this);
216        buf.attribute("type", getType());
217        buf.rightAngelBracket();
218
219        buf.optElement("title", getTitle());
220        for (String instruction : getInstructions()) {
221            buf.element("instructions", instruction);
222        }
223        // Append the list of fields returned from a search
224        if (getReportedData() != null) {
225            buf.append(getReportedData().toXML());
226        }
227        // Loop through all the items returned from a search and append them to the string buffer
228        for (Item item : getItems()) {
229            buf.append(item.toXML());
230        }
231        // Loop through all the form fields and append them to the string buffer
232        for (FormField field : getFields()) {
233            buf.append(field.toXML());
234        }
235        buf.closeElement(this);
236        return buf;
237    }
238
239    /**
240     * 
241     * Represents the fields that will be returned from a search. This information is useful when 
242     * you try to use the jabber:iq:search namespace to return dynamic form information.
243     *
244     * @author Gaston Dombiak
245     */
246    public static class ReportedData {
247        public static final String ELEMENT = "reported";
248
249        private List<FormField> fields = new ArrayList<FormField>();
250        
251        public ReportedData(List<FormField> fields) {
252            this.fields = fields;
253        }
254
255        /**
256         * Returns the fields returned from a search.
257         * 
258         * @return the fields returned from a search.
259         */
260        public List<FormField> getFields() {
261            return Collections.unmodifiableList(new ArrayList<FormField>(fields));
262        }
263
264        public CharSequence toXML() {
265            XmlStringBuilder buf = new XmlStringBuilder();
266            buf.openElement(ELEMENT);
267            // Loop through all the form items and append them to the string buffer
268            for (FormField field : getFields()) {
269                buf.append(field.toXML());
270            }
271            buf.closeElement(ELEMENT);
272            return buf;
273        }
274    }
275    
276    /**
277     * 
278     * Represents items of reported data.
279     *
280     * @author Gaston Dombiak
281     */
282    public static class Item {
283        public static final String ELEMENT = "item";
284
285        private List<FormField> fields = new ArrayList<FormField>();
286        
287        public Item(List<FormField> fields) {
288            this.fields = fields;
289        }
290        
291        /**
292         * Returns the fields that define the data that goes with the item.
293         * 
294         * @return the fields that define the data that goes with the item.
295         */
296        public List<FormField> getFields() {
297            return Collections.unmodifiableList(new ArrayList<FormField>(fields));
298        }
299        
300        public CharSequence toXML() {
301            XmlStringBuilder buf = new XmlStringBuilder();
302            buf.openElement(ELEMENT);
303            // Loop through all the form items and append them to the string buffer
304            for (FormField field : getFields()) {
305                buf.append(field.toXML());
306            }
307            buf.closeElement(ELEMENT);
308            return buf;
309        }
310    }
311}