1/**
2 * $RCSfile$
3 * $Revision$
4 * $Date$
5 *
6 * Copyright 2003-2007 Jive Software.
7 *
8 * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 *     http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21package org.jivesoftware.smackx.packet;
22
23import org.jivesoftware.smack.packet.PacketExtension;
24import org.jivesoftware.smackx.Form;
25import org.jivesoftware.smackx.FormField;
26
27import java.util.ArrayList;
28import java.util.Collections;
29import java.util.Iterator;
30import java.util.List;
31
32/**
33 * Represents a form that could be use for gathering data as well as for reporting data
34 * returned from a search.
35 *
36 * @author Gaston Dombiak
37 */
38public class DataForm implements PacketExtension {
39
40    private String type;
41    private String title;
42    private List<String> instructions = new ArrayList<String>();
43    private ReportedData reportedData;
44    private final List<Item> items = new ArrayList<Item>();
45    private final List<FormField> fields = new ArrayList<FormField>();
46
47    public DataForm(String type) {
48        this.type = type;
49    }
50
51    /**
52     * Returns the meaning of the data within the context. The data could be part of a form
53     * to fill out, a form submission or data results.<p>
54     *
55     * Possible form types are:
56     * <ul>
57     *  <li>form -> This packet contains a form to fill out. Display it to the user (if your
58     * program can).</li>
59     *  <li>submit -> The form is filled out, and this is the data that is being returned from
60     * the form.</li>
61     *  <li>cancel -> The form was cancelled. Tell the asker that piece of information.</li>
62     *  <li>result -> Data results being returned from a search, or some other query.</li>
63     * </ul>
64     *
65     * @return the form's type.
66     */
67    public String getType() {
68        return type;
69    }
70
71    /**
72     * Returns the description of the data. It is similar to the title on a web page or an X
73     * window.  You can put a <title/> on either a form to fill out, or a set of data results.
74     *
75     * @return description of the data.
76     */
77    public String getTitle() {
78        return title;
79    }
80
81    /**
82     * Returns an Iterator for the list of instructions that explain how to fill out the form and
83     * what the form is about. The dataform could include multiple instructions since each
84     * instruction could not contain newlines characters. Join the instructions together in order
85     * to show them to the user.
86     *
87     * @return an Iterator for the list of instructions that explain how to fill out the form.
88     */
89    public Iterator<String> getInstructions() {
90        synchronized (instructions) {
91            return Collections.unmodifiableList(new ArrayList<String>(instructions)).iterator();
92        }
93    }
94
95    /**
96     * Returns the fields that will be returned from a search.
97     *
98     * @return fields that will be returned from a search.
99     */
100    public ReportedData getReportedData() {
101        return reportedData;
102    }
103
104    /**
105     * Returns an Iterator for the items returned from a search.
106     *
107     * @return an Iterator for the items returned from a search.
108     */
109    public Iterator<Item> getItems() {
110        synchronized (items) {
111            return Collections.unmodifiableList(new ArrayList<Item>(items)).iterator();
112        }
113    }
114
115    /**
116     * Returns an Iterator for the fields that are part of the form.
117     *
118     * @return an Iterator for the fields that are part of the form.
119     */
120    public Iterator<FormField> getFields() {
121        synchronized (fields) {
122            return Collections.unmodifiableList(new ArrayList<FormField>(fields)).iterator();
123        }
124    }
125
126    public String getElementName() {
127        return Form.ELEMENT;
128    }
129
130    public String getNamespace() {
131        return Form.NAMESPACE;
132    }
133
134    /**
135     * Sets the description of the data. It is similar to the title on a web page or an X window.
136     * You can put a <title/> on either a form to fill out, or a set of data results.
137     *
138     * @param title description of the data.
139     */
140    public void setTitle(String title) {
141        this.title = title;
142    }
143
144    /**
145     * Sets the list of instructions that explain how to fill out the form and what the form is
146     * about. The dataform could include multiple instructions since each instruction could not
147     * contain newlines characters.
148     *
149     * @param instructions list of instructions that explain how to fill out the form.
150     */
151    public void setInstructions(List<String> instructions) {
152        this.instructions = instructions;
153    }
154
155    /**
156     * Sets the fields that will be returned from a search.
157     *
158     * @param reportedData the fields that will be returned from a search.
159     */
160    public void setReportedData(ReportedData reportedData) {
161        this.reportedData = reportedData;
162    }
163
164    /**
165     * Adds a new field as part of the form.
166     *
167     * @param field the field to add to the form.
168     */
169    public void addField(FormField field) {
170        synchronized (fields) {
171            fields.add(field);
172        }
173    }
174
175    /**
176     * Adds a new instruction to the list of instructions that explain how to fill out the form
177     * and what the form is about. The dataform could include multiple instructions since each
178     * instruction could not contain newlines characters.
179     *
180     * @param instruction the new instruction that explain how to fill out the form.
181     */
182    public void addInstruction(String instruction) {
183        synchronized (instructions) {
184            instructions.add(instruction);
185        }
186    }
187
188    /**
189     * Adds a new item returned from a search.
190     *
191     * @param item the item returned from a search.
192     */
193    public void addItem(Item item) {
194        synchronized (items) {
195            items.add(item);
196        }
197    }
198
199    /**
200     * Returns true if this DataForm has at least one FORM_TYPE field which is
201     * hidden. This method is used for sanity checks.
202     *
203     * @return
204     */
205    public boolean hasHiddenFormTypeField() {
206        boolean found = false;
207        for (FormField f : fields) {
208            if (f.getVariable().equals("FORM_TYPE") && f.getType() != null && f.getType().equals("hidden"))
209                found = true;
210        }
211        return found;
212    }
213
214    public String toXML() {
215        StringBuilder buf = new StringBuilder();
216        buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append(
217            "\" type=\"" + getType() +"\">");
218        if (getTitle() != null) {
219            buf.append("<title>").append(getTitle()).append("</title>");
220        }
221        for (Iterator<String> it=getInstructions(); it.hasNext();) {
222            buf.append("<instructions>").append(it.next()).append("</instructions>");
223        }
224        // Append the list of fields returned from a search
225        if (getReportedData() != null) {
226            buf.append(getReportedData().toXML());
227        }
228        // Loop through all the items returned from a search and append them to the string buffer
229        for (Iterator<Item> i = getItems(); i.hasNext();) {
230            Item item = i.next();
231            buf.append(item.toXML());
232        }
233        // Loop through all the form fields and append them to the string buffer
234        for (Iterator<FormField> i = getFields(); i.hasNext();) {
235            FormField field = i.next();
236            buf.append(field.toXML());
237        }
238        buf.append("</").append(getElementName()).append(">");
239        return buf.toString();
240    }
241
242    /**
243     *
244     * Represents the fields that will be returned from a search. This information is useful when
245     * you try to use the jabber:iq:search namespace to return dynamic form information.
246     *
247     * @author Gaston Dombiak
248     */
249    public static class ReportedData {
250        private List<FormField> fields = new ArrayList<FormField>();
251
252        public ReportedData(List<FormField> fields) {
253            this.fields = fields;
254        }
255
256        /**
257         * Returns the fields returned from a search.
258         *
259         * @return the fields returned from a search.
260         */
261        public Iterator<FormField> getFields() {
262            return Collections.unmodifiableList(new ArrayList<FormField>(fields)).iterator();
263        }
264
265        public String toXML() {
266            StringBuilder buf = new StringBuilder();
267            buf.append("<reported>");
268            // Loop through all the form items and append them to the string buffer
269            for (Iterator<FormField> i = getFields(); i.hasNext();) {
270                FormField field = i.next();
271                buf.append(field.toXML());
272            }
273            buf.append("</reported>");
274            return buf.toString();
275        }
276    }
277
278    /**
279     *
280     * Represents items of reported data.
281     *
282     * @author Gaston Dombiak
283     */
284    public static class Item {
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 Iterator<FormField> getFields() {
297            return Collections.unmodifiableList(new ArrayList<FormField>(fields)).iterator();
298        }
299
300        public String toXML() {
301            StringBuilder buf = new StringBuilder();
302            buf.append("<item>");
303            // Loop through all the form items and append them to the string buffer
304            for (Iterator<FormField> i = getFields(); i.hasNext();) {
305                FormField field = i.next();
306                buf.append(field.toXML());
307            }
308            buf.append("</item>");
309            return buf.toString();
310        }
311    }
312}
313