1/**
2 * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
3 * you may not use this file except in compliance with the License.
4 * You may obtain a copy of the License at
5 *
6 *     http://www.apache.org/licenses/LICENSE-2.0
7 *
8 * Unless required by applicable law or agreed to in writing, software
9 * distributed under the License is distributed on an "AS IS" BASIS,
10 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 * See the License for the specific language governing permissions and
12 * limitations under the License.
13 */
14package org.jivesoftware.smackx.pubsub;
15
16import java.util.ArrayList;
17import java.util.Iterator;
18import java.util.List;
19
20import org.jivesoftware.smackx.Form;
21import org.jivesoftware.smackx.FormField;
22import org.jivesoftware.smackx.packet.DataForm;
23
24/**
25 * A decorator for a {@link Form} to easily enable reading and updating
26 * of node configuration.  All operations read or update the underlying {@link DataForm}.
27 *
28 * <p>Unlike the {@link Form}.setAnswer(XXX)} methods, which throw an exception if the field does not
29 * exist, all <b>ConfigureForm.setXXX</b> methods will create the field in the wrapped form
30 * if it does not already exist.
31 *
32 * @author Robin Collier
33 */
34public class ConfigureForm extends Form
35{
36	/**
37	 * Create a decorator from an existing {@link DataForm} that has been
38	 * retrieved from parsing a node configuration request.
39	 *
40	 * @param configDataForm
41	 */
42	public ConfigureForm(DataForm configDataForm)
43	{
44		super(configDataForm);
45	}
46
47	/**
48	 * Create a decorator from an existing {@link Form} for node configuration.
49	 * Typically, this can be used to create a decorator for an answer form
50	 * by using the result of {@link #createAnswerForm()} as the input parameter.
51	 *
52	 * @param nodeConfigForm
53	 */
54	public ConfigureForm(Form nodeConfigForm)
55	{
56		super(nodeConfigForm.getDataFormToSend());
57	}
58
59	/**
60	 * Create a new form for configuring a node.  This would typically only be used
61	 * when creating and configuring a node at the same time via {@link PubSubManager#createNode(String, Form)}, since
62	 * configuration of an existing node is typically accomplished by calling {@link LeafNode#getNodeConfiguration()} and
63	 * using the resulting form to create a answer form.  See {@link #ConfigureForm(Form)}.
64	 * @param formType
65	 */
66	public ConfigureForm(FormType formType)
67	{
68		super(formType.toString());
69	}
70
71	/**
72	 * Get the currently configured {@link AccessModel}, null if it is not set.
73	 *
74	 * @return The current {@link AccessModel}
75	 */
76	public AccessModel getAccessModel()
77	{
78		String value = getFieldValue(ConfigureNodeFields.access_model);
79
80		if (value == null)
81			return null;
82		else
83			return AccessModel.valueOf(value);
84	}
85
86	/**
87	 * Sets the value of access model.
88	 *
89	 * @param accessModel
90	 */
91	public void setAccessModel(AccessModel accessModel)
92	{
93		addField(ConfigureNodeFields.access_model, FormField.TYPE_LIST_SINGLE);
94		setAnswer(ConfigureNodeFields.access_model.getFieldName(), getListSingle(accessModel.toString()));
95	}
96
97	/**
98	 * Returns the URL of an XSL transformation which can be applied to payloads in order to
99	 * generate an appropriate message body element.
100	 *
101	 * @return URL to an XSL
102	 */
103	public String getBodyXSLT()
104	{
105		return getFieldValue(ConfigureNodeFields.body_xslt);
106	}
107
108	/**
109	 * Set the URL of an XSL transformation which can be applied to payloads in order to
110	 * generate an appropriate message body element.
111	 *
112	 * @param bodyXslt The URL of an XSL
113	 */
114	public void setBodyXSLT(String bodyXslt)
115	{
116		addField(ConfigureNodeFields.body_xslt, FormField.TYPE_TEXT_SINGLE);
117		setAnswer(ConfigureNodeFields.body_xslt.getFieldName(), bodyXslt);
118	}
119
120	/**
121	 * The id's of the child nodes associated with a collection node (both leaf and collection).
122	 *
123	 * @return Iterator over the list of child nodes.
124	 */
125	public Iterator<String> getChildren()
126	{
127		return getFieldValues(ConfigureNodeFields.children);
128	}
129
130	/**
131	 * Set the list of child node ids that are associated with a collection node.
132	 *
133	 * @param children
134	 */
135	public void setChildren(List<String> children)
136	{
137		addField(ConfigureNodeFields.children, FormField.TYPE_TEXT_MULTI);
138		setAnswer(ConfigureNodeFields.children.getFieldName(), children);
139	}
140
141	/**
142	 * Returns the policy that determines who may associate children with the node.
143	 *
144	 * @return The current policy
145	 */
146	public ChildrenAssociationPolicy getChildrenAssociationPolicy()
147	{
148		String value = getFieldValue(ConfigureNodeFields.children_association_policy);
149
150		if (value == null)
151			return null;
152		else
153			return ChildrenAssociationPolicy.valueOf(value);
154	}
155
156	/**
157	 * Sets the policy that determines who may associate children with the node.
158	 *
159	 * @param policy The policy being set
160	 */
161	public void setChildrenAssociationPolicy(ChildrenAssociationPolicy policy)
162	{
163		addField(ConfigureNodeFields.children_association_policy, FormField.TYPE_LIST_SINGLE);
164        List<String> values = new ArrayList<String>(1);
165        values.add(policy.toString());
166        setAnswer(ConfigureNodeFields.children_association_policy.getFieldName(), values);
167	}
168
169	/**
170	 * Iterator of JID's that are on the whitelist that determines who can associate child nodes
171	 * with the collection node.  This is only relevant if {@link #getChildrenAssociationPolicy()} is set to
172	 * {@link ChildrenAssociationPolicy#whitelist}.
173	 *
174	 * @return Iterator over whitelist
175	 */
176	public Iterator<String> getChildrenAssociationWhitelist()
177	{
178		return getFieldValues(ConfigureNodeFields.children_association_whitelist);
179	}
180
181	/**
182	 * Set the JID's in the whitelist of users that can associate child nodes with the collection
183	 * node.  This is only relevant if {@link #getChildrenAssociationPolicy()} is set to
184	 * {@link ChildrenAssociationPolicy#whitelist}.
185	 *
186	 * @param whitelist The list of JID's
187	 */
188	public void setChildrenAssociationWhitelist(List<String> whitelist)
189	{
190		addField(ConfigureNodeFields.children_association_whitelist, FormField.TYPE_JID_MULTI);
191		setAnswer(ConfigureNodeFields.children_association_whitelist.getFieldName(), whitelist);
192	}
193
194	/**
195	 * Gets the maximum number of child nodes that can be associated with the collection node.
196	 *
197	 * @return The maximum number of child nodes
198	 */
199	public int getChildrenMax()
200	{
201		return Integer.parseInt(getFieldValue(ConfigureNodeFields.children_max));
202	}
203
204	/**
205	 * Set the maximum number of child nodes that can be associated with a collection node.
206	 *
207	 * @param max The maximum number of child nodes.
208	 */
209	public void setChildrenMax(int max)
210	{
211		addField(ConfigureNodeFields.children_max, FormField.TYPE_TEXT_SINGLE);
212		setAnswer(ConfigureNodeFields.children_max.getFieldName(), max);
213	}
214
215	/**
216	 * Gets the collection node which the node is affiliated with.
217	 *
218	 * @return The collection node id
219	 */
220	public String getCollection()
221	{
222		return getFieldValue(ConfigureNodeFields.collection);
223	}
224
225	/**
226	 * Sets the collection node which the node is affiliated with.
227	 *
228	 * @param collection The node id of the collection node
229	 */
230	public void setCollection(String collection)
231	{
232		addField(ConfigureNodeFields.collection, FormField.TYPE_TEXT_SINGLE);
233		setAnswer(ConfigureNodeFields.collection.getFieldName(), collection);
234	}
235
236	/**
237	 * Gets the URL of an XSL transformation which can be applied to the payload
238	 * format in order to generate a valid Data Forms result that the client could
239	 * display using a generic Data Forms rendering engine.
240	 *
241	 * @return The URL of an XSL transformation
242	 */
243	public String getDataformXSLT()
244	{
245		return getFieldValue(ConfigureNodeFields.dataform_xslt);
246	}
247
248	/**
249	 * Sets the URL of an XSL transformation which can be applied to the payload
250	 * format in order to generate a valid Data Forms result that the client could
251	 * display using a generic Data Forms rendering engine.
252	 *
253	 * @param url The URL of an XSL transformation
254	 */
255	public void setDataformXSLT(String url)
256	{
257		addField(ConfigureNodeFields.dataform_xslt, FormField.TYPE_TEXT_SINGLE);
258		setAnswer(ConfigureNodeFields.dataform_xslt.getFieldName(), url);
259	}
260
261	/**
262	 * Does the node deliver payloads with event notifications.
263	 *
264	 * @return true if it does, false otherwise
265	 */
266	public boolean isDeliverPayloads()
267	{
268		return parseBoolean(getFieldValue(ConfigureNodeFields.deliver_payloads));
269	}
270
271	/**
272	 * Sets whether the node will deliver payloads with event notifications.
273	 *
274	 * @param deliver true if the payload will be delivered, false otherwise
275	 */
276	public void setDeliverPayloads(boolean deliver)
277	{
278		addField(ConfigureNodeFields.deliver_payloads, FormField.TYPE_BOOLEAN);
279		setAnswer(ConfigureNodeFields.deliver_payloads.getFieldName(), deliver);
280	}
281
282	/**
283	 * Determines who should get replies to items
284	 *
285	 * @return Who should get the reply
286	 */
287	public ItemReply getItemReply()
288	{
289		String value = getFieldValue(ConfigureNodeFields.itemreply);
290
291		if (value == null)
292			return null;
293		else
294			return ItemReply.valueOf(value);
295	}
296
297	/**
298	 * Sets who should get the replies to items
299	 *
300	 * @param reply Defines who should get the reply
301	 */
302	public void setItemReply(ItemReply reply)
303	{
304		addField(ConfigureNodeFields.itemreply, FormField.TYPE_LIST_SINGLE);
305		setAnswer(ConfigureNodeFields.itemreply.getFieldName(), getListSingle(reply.toString()));
306	}
307
308	/**
309	 * Gets the maximum number of items to persisted to this node if {@link #isPersistItems()} is
310	 * true.
311	 *
312	 * @return The maximum number of items to persist
313	 */
314	public int getMaxItems()
315	{
316		return Integer.parseInt(getFieldValue(ConfigureNodeFields.max_items));
317	}
318
319	/**
320	 * Set the maximum number of items to persisted to this node if {@link #isPersistItems()} is
321	 * true.
322	 *
323	 * @param max The maximum number of items to persist
324	 */
325	public void setMaxItems(int max)
326	{
327		addField(ConfigureNodeFields.max_items, FormField.TYPE_TEXT_SINGLE);
328		setAnswer(ConfigureNodeFields.max_items.getFieldName(), max);
329	}
330
331	/**
332	 * Gets the maximum payload size in bytes.
333	 *
334	 * @return The maximum payload size
335	 */
336	public int getMaxPayloadSize()
337	{
338		return Integer.parseInt(getFieldValue(ConfigureNodeFields.max_payload_size));
339	}
340
341	/**
342	 * Sets the maximum payload size in bytes
343	 *
344	 * @param max The maximum payload size
345	 */
346	public void setMaxPayloadSize(int max)
347	{
348		addField(ConfigureNodeFields.max_payload_size, FormField.TYPE_TEXT_SINGLE);
349		setAnswer(ConfigureNodeFields.max_payload_size.getFieldName(), max);
350	}
351
352	/**
353	 * Gets the node type
354	 *
355	 * @return The node type
356	 */
357	public NodeType getNodeType()
358	{
359		String value = getFieldValue(ConfigureNodeFields.node_type);
360
361		if (value == null)
362			return null;
363		else
364			return NodeType.valueOf(value);
365	}
366
367	/**
368	 * Sets the node type
369	 *
370	 * @param type The node type
371	 */
372	public void setNodeType(NodeType type)
373	{
374		addField(ConfigureNodeFields.node_type, FormField.TYPE_LIST_SINGLE);
375		setAnswer(ConfigureNodeFields.node_type.getFieldName(), getListSingle(type.toString()));
376	}
377
378	/**
379	 * Determines if subscribers should be notified when the configuration changes.
380	 *
381	 * @return true if they should be notified, false otherwise
382	 */
383	public boolean isNotifyConfig()
384	{
385		return parseBoolean(getFieldValue(ConfigureNodeFields.notify_config));
386	}
387
388	/**
389	 * Sets whether subscribers should be notified when the configuration changes.
390	 *
391	 * @param notify true if subscribers should be notified, false otherwise
392	 */
393	public void setNotifyConfig(boolean notify)
394	{
395		addField(ConfigureNodeFields.notify_config, FormField.TYPE_BOOLEAN);
396		setAnswer(ConfigureNodeFields.notify_config.getFieldName(), notify);
397	}
398
399	/**
400	 * Determines whether subscribers should be notified when the node is deleted.
401	 *
402	 * @return true if subscribers should be notified, false otherwise
403	 */
404	public boolean isNotifyDelete()
405	{
406		return parseBoolean(getFieldValue(ConfigureNodeFields.notify_delete));
407	}
408
409	/**
410	 * Sets whether subscribers should be notified when the node is deleted.
411	 *
412	 * @param notify true if subscribers should be notified, false otherwise
413	 */
414	public void setNotifyDelete(boolean notify)
415	{
416		addField(ConfigureNodeFields.notify_delete, FormField.TYPE_BOOLEAN);
417		setAnswer(ConfigureNodeFields.notify_delete.getFieldName(), notify);
418	}
419
420	/**
421	 * Determines whether subscribers should be notified when items are deleted
422	 * from the node.
423	 *
424	 * @return true if subscribers should be notified, false otherwise
425	 */
426	public boolean isNotifyRetract()
427	{
428		return parseBoolean(getFieldValue(ConfigureNodeFields.notify_retract));
429	}
430
431	/**
432	 * Sets whether subscribers should be notified when items are deleted
433	 * from the node.
434	 *
435	 * @param notify true if subscribers should be notified, false otherwise
436	 */
437	public void setNotifyRetract(boolean notify)
438	{
439		addField(ConfigureNodeFields.notify_retract, FormField.TYPE_BOOLEAN);
440		setAnswer(ConfigureNodeFields.notify_retract.getFieldName(), notify);
441	}
442
443	/**
444	 * Determines whether items should be persisted in the node.
445	 *
446	 * @return true if items are persisted
447	 */
448	public boolean isPersistItems()
449	{
450		return parseBoolean(getFieldValue(ConfigureNodeFields.persist_items));
451	}
452
453	/**
454	 * Sets whether items should be persisted in the node.
455	 *
456	 * @param persist true if items should be persisted, false otherwise
457	 */
458	public void setPersistentItems(boolean persist)
459	{
460		addField(ConfigureNodeFields.persist_items, FormField.TYPE_BOOLEAN);
461		setAnswer(ConfigureNodeFields.persist_items.getFieldName(), persist);
462	}
463
464	/**
465	 * Determines whether to deliver notifications to available users only.
466	 *
467	 * @return true if users must be available
468	 */
469	public boolean isPresenceBasedDelivery()
470	{
471		return parseBoolean(getFieldValue(ConfigureNodeFields.presence_based_delivery));
472	}
473
474	/**
475	 * Sets whether to deliver notifications to available users only.
476	 *
477	 * @param presenceBased true if user must be available, false otherwise
478	 */
479	public void setPresenceBasedDelivery(boolean presenceBased)
480	{
481		addField(ConfigureNodeFields.presence_based_delivery, FormField.TYPE_BOOLEAN);
482		setAnswer(ConfigureNodeFields.presence_based_delivery.getFieldName(), presenceBased);
483	}
484
485	/**
486	 * Gets the publishing model for the node, which determines who may publish to it.
487	 *
488	 * @return The publishing model
489	 */
490	public PublishModel getPublishModel()
491	{
492		String value = getFieldValue(ConfigureNodeFields.publish_model);
493
494		if (value == null)
495			return null;
496		else
497			return PublishModel.valueOf(value);
498	}
499
500	/**
501	 * Sets the publishing model for the node, which determines who may publish to it.
502	 *
503	 * @param publish The enum representing the possible options for the publishing model
504	 */
505	public void setPublishModel(PublishModel publish)
506	{
507		addField(ConfigureNodeFields.publish_model, FormField.TYPE_LIST_SINGLE);
508		setAnswer(ConfigureNodeFields.publish_model.getFieldName(), getListSingle(publish.toString()));
509	}
510
511	/**
512	 * Iterator over the multi user chat rooms that are specified as reply rooms.
513	 *
514	 * @return The reply room JID's
515	 */
516	public Iterator<String> getReplyRoom()
517	{
518		return getFieldValues(ConfigureNodeFields.replyroom);
519	}
520
521	/**
522	 * Sets the multi user chat rooms that are specified as reply rooms.
523	 *
524	 * @param replyRooms The multi user chat room to use as reply rooms
525	 */
526	public void setReplyRoom(List<String> replyRooms)
527	{
528		addField(ConfigureNodeFields.replyroom, FormField.TYPE_LIST_MULTI);
529		setAnswer(ConfigureNodeFields.replyroom.getFieldName(), replyRooms);
530	}
531
532	/**
533	 * Gets the specific JID's for reply to.
534	 *
535	 * @return The JID's
536	 */
537	public Iterator<String> getReplyTo()
538	{
539		return getFieldValues(ConfigureNodeFields.replyto);
540	}
541
542	/**
543	 * Sets the specific JID's for reply to.
544	 *
545	 * @param replyTos The JID's to reply to
546	 */
547	public void setReplyTo(List<String> replyTos)
548	{
549		addField(ConfigureNodeFields.replyto, FormField.TYPE_LIST_MULTI);
550		setAnswer(ConfigureNodeFields.replyto.getFieldName(), replyTos);
551	}
552
553	/**
554	 * Gets the roster groups that are allowed to subscribe and retrieve items.
555	 *
556	 * @return The roster groups
557	 */
558	public Iterator<String> getRosterGroupsAllowed()
559	{
560		return getFieldValues(ConfigureNodeFields.roster_groups_allowed);
561	}
562
563	/**
564	 * Sets the roster groups that are allowed to subscribe and retrieve items.
565	 *
566	 * @param groups The roster groups
567	 */
568	public void setRosterGroupsAllowed(List<String> groups)
569	{
570		addField(ConfigureNodeFields.roster_groups_allowed, FormField.TYPE_LIST_MULTI);
571		setAnswer(ConfigureNodeFields.roster_groups_allowed.getFieldName(), groups);
572	}
573
574	/**
575	 * Determines if subscriptions are allowed.
576	 *
577	 * @return true if subscriptions are allowed, false otherwise
578	 */
579	public boolean isSubscibe()
580	{
581		return parseBoolean(getFieldValue(ConfigureNodeFields.subscribe));
582	}
583
584	/**
585	 * Sets whether subscriptions are allowed.
586	 *
587	 * @param subscribe true if they are, false otherwise
588	 */
589	public void setSubscribe(boolean subscribe)
590	{
591		addField(ConfigureNodeFields.subscribe, FormField.TYPE_BOOLEAN);
592		setAnswer(ConfigureNodeFields.subscribe.getFieldName(), subscribe);
593	}
594
595	/**
596	 * Gets the human readable node title.
597	 *
598	 * @return The node title
599	 */
600	public String getTitle()
601	{
602		return getFieldValue(ConfigureNodeFields.title);
603	}
604
605	/**
606	 * Sets a human readable title for the node.
607	 *
608	 * @param title The node title
609	 */
610	public void setTitle(String title)
611	{
612		addField(ConfigureNodeFields.title, FormField.TYPE_TEXT_SINGLE);
613		setAnswer(ConfigureNodeFields.title.getFieldName(), title);
614	}
615
616	/**
617	 * The type of node data, usually specified by the namespace of the payload (if any).
618	 *
619	 * @return The type of node data
620	 */
621	public String getDataType()
622	{
623		return getFieldValue(ConfigureNodeFields.type);
624	}
625
626	/**
627	 * Sets the type of node data, usually specified by the namespace of the payload (if any).
628	 *
629	 * @param type The type of node data
630	 */
631	public void setDataType(String type)
632	{
633		addField(ConfigureNodeFields.type, FormField.TYPE_TEXT_SINGLE);
634		setAnswer(ConfigureNodeFields.type.getFieldName(), type);
635	}
636
637	@Override
638	public String toString()
639	{
640		StringBuilder result = new StringBuilder(getClass().getName() + " Content [");
641
642		Iterator<FormField> fields = getFields();
643
644		while (fields.hasNext())
645		{
646			FormField formField = fields.next();
647			result.append('(');
648			result.append(formField.getVariable());
649			result.append(':');
650
651			Iterator<String> values = formField.getValues();
652			StringBuilder valuesBuilder = new StringBuilder();
653
654			while (values.hasNext())
655			{
656				if (valuesBuilder.length() > 0)
657					result.append(',');
658				String value = (String)values.next();
659				valuesBuilder.append(value);
660			}
661
662			if (valuesBuilder.length() == 0)
663				valuesBuilder.append("NOT SET");
664			result.append(valuesBuilder);
665			result.append(')');
666		}
667		result.append(']');
668		return result.toString();
669	}
670
671	static private boolean parseBoolean(String fieldValue)
672	{
673		return ("1".equals(fieldValue) || "true".equals(fieldValue));
674	}
675
676	private String getFieldValue(ConfigureNodeFields field)
677	{
678		FormField formField = getField(field.getFieldName());
679
680		return (formField.getValues().hasNext()) ? formField.getValues().next() : null;
681	}
682
683	private Iterator<String> getFieldValues(ConfigureNodeFields field)
684	{
685		FormField formField = getField(field.getFieldName());
686
687		return formField.getValues();
688	}
689
690	private void addField(ConfigureNodeFields nodeField, String type)
691	{
692		String fieldName = nodeField.getFieldName();
693
694		if (getField(fieldName) == null)
695		{
696			FormField field = new FormField(fieldName);
697			field.setType(type);
698			addField(field);
699		}
700	}
701
702	private List<String> getListSingle(String value)
703	{
704		List<String> list = new ArrayList<String>(1);
705		list.add(value);
706		return list;
707	}
708
709}
710