001/**
002 *
003 * Copyright the original author or authors
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 */
017package org.jivesoftware.smackx.pubsub;
018
019import java.util.ArrayList;
020import java.util.List;
021
022import org.jivesoftware.smackx.xdata.Form;
023import org.jivesoftware.smackx.xdata.FormField;
024import org.jivesoftware.smackx.xdata.packet.DataForm;
025
026/**
027 * A decorator for a {@link Form} to easily enable reading and updating
028 * of node configuration.  All operations read or update the underlying {@link DataForm}.
029 * 
030 * <p>Unlike the {@link Form}.setAnswer(XXX)} methods, which throw an exception if the field does not
031 * exist, all <b>ConfigureForm.setXXX</b> methods will create the field in the wrapped form
032 * if it does not already exist. 
033 * 
034 * @author Robin Collier
035 */
036public class ConfigureForm extends Form
037{
038    /**
039     * Create a decorator from an existing {@link DataForm} that has been
040     * retrieved from parsing a node configuration request.
041     * 
042     * @param configDataForm
043     */
044    public ConfigureForm(DataForm configDataForm)
045    {
046        super(configDataForm);
047    }
048
049    /**
050     * Create a decorator from an existing {@link Form} for node configuration.
051     * Typically, this can be used to create a decorator for an answer form
052     * by using the result of {@link #createAnswerForm()} as the input parameter.
053     * 
054     * @param nodeConfigForm
055     */
056    public ConfigureForm(Form nodeConfigForm)
057    {
058        super(nodeConfigForm.getDataFormToSend());
059    }
060
061    /**
062     * Create a new form for configuring a node.  This would typically only be used 
063     * when creating and configuring a node at the same time via {@link PubSubManager#createNode(String, Form)}, since 
064     * configuration of an existing node is typically accomplished by calling {@link LeafNode#getNodeConfiguration()} and
065     * using the resulting form to create a answer form.  See {@link #ConfigureForm(Form)}.
066     * @param formType
067     */
068    public ConfigureForm(DataForm.Type formType)
069    {
070        super(formType);
071    }
072
073    /**
074     * Get the currently configured {@link AccessModel}, null if it is not set.
075     * 
076     * @return The current {@link AccessModel}
077     */
078    public AccessModel getAccessModel()
079    {
080        String value = getFieldValue(ConfigureNodeFields.access_model);
081
082        if (value == null)
083            return null;
084        else
085            return AccessModel.valueOf(value);
086    }
087
088    /**
089     * Sets the value of access model.
090     * 
091     * @param accessModel
092     */
093    public void setAccessModel(AccessModel accessModel)
094    {
095        addField(ConfigureNodeFields.access_model, FormField.Type.list_single);
096        setAnswer(ConfigureNodeFields.access_model.getFieldName(), getListSingle(accessModel.toString()));
097    }
098
099    /**
100     * Returns the URL of an XSL transformation which can be applied to payloads in order to 
101     * generate an appropriate message body element.
102     * 
103     * @return URL to an XSL
104     */
105    public String getBodyXSLT()
106    {
107        return getFieldValue(ConfigureNodeFields.body_xslt);
108    }
109
110    /**
111     * Set the URL of an XSL transformation which can be applied to payloads in order to 
112     * generate an appropriate message body element.
113     * 
114     * @param bodyXslt The URL of an XSL
115     */
116    public void setBodyXSLT(String bodyXslt)
117    {
118        addField(ConfigureNodeFields.body_xslt, FormField.Type.text_single);
119        setAnswer(ConfigureNodeFields.body_xslt.getFieldName(), bodyXslt);
120    }
121
122    /**
123     * The id's of the child nodes associated with a collection node (both leaf and collection).
124     * 
125     * @return list of child nodes.
126     */
127    public List<String> getChildren()
128    {
129        return getFieldValues(ConfigureNodeFields.children);
130    }
131
132    /**
133     * Set the list of child node ids that are associated with a collection node.
134     * 
135     * @param children
136     */
137    public void setChildren(List<String> children)
138    {
139        addField(ConfigureNodeFields.children, FormField.Type.text_multi);
140        setAnswer(ConfigureNodeFields.children.getFieldName(), children);
141    }
142
143    /**
144     * Returns the policy that determines who may associate children with the node.
145     *  
146     * @return The current policy
147     */
148    public ChildrenAssociationPolicy getChildrenAssociationPolicy()
149    {
150        String value = getFieldValue(ConfigureNodeFields.children_association_policy);
151
152        if (value == null)
153            return null;
154        else
155            return ChildrenAssociationPolicy.valueOf(value);
156    }
157
158    /**
159     * Sets the policy that determines who may associate children with the node.
160     * 
161     * @param policy The policy being set
162     */
163    public void setChildrenAssociationPolicy(ChildrenAssociationPolicy policy)
164    {
165        addField(ConfigureNodeFields.children_association_policy, FormField.Type.list_single);
166        List<String> values = new ArrayList<>(1);
167        values.add(policy.toString());
168        setAnswer(ConfigureNodeFields.children_association_policy.getFieldName(), values);
169    }
170
171    /**
172     * List of JID's that are on the whitelist that determines who can associate child nodes 
173     * with the collection node.  This is only relevant if {@link #getChildrenAssociationPolicy()} is set to
174     * {@link ChildrenAssociationPolicy#whitelist}.
175     * 
176     * @return List of the whitelist
177     */
178    public List<String> getChildrenAssociationWhitelist()
179    {
180        return getFieldValues(ConfigureNodeFields.children_association_whitelist);
181    }
182
183    /**
184     * Set the JID's in the whitelist of users that can associate child nodes with the collection 
185     * node.  This is only relevant if {@link #getChildrenAssociationPolicy()} is set to
186     * {@link ChildrenAssociationPolicy#whitelist}.
187     * 
188     * @param whitelist The list of JID's
189     */
190    public void setChildrenAssociationWhitelist(List<String> whitelist)
191    {
192        addField(ConfigureNodeFields.children_association_whitelist, FormField.Type.jid_multi);
193        setAnswer(ConfigureNodeFields.children_association_whitelist.getFieldName(), whitelist);
194    }
195
196    /**
197     * Gets the maximum number of child nodes that can be associated with the collection node.
198     * 
199     * @return The maximum number of child nodes
200     */
201    public int getChildrenMax()
202    {
203        return Integer.parseInt(getFieldValue(ConfigureNodeFields.children_max));
204    }
205
206    /**
207     * Set the maximum number of child nodes that can be associated with a collection node.
208     * 
209     * @param max The maximum number of child nodes.
210     */
211    public void setChildrenMax(int max)
212    {
213        addField(ConfigureNodeFields.children_max, FormField.Type.text_single);
214        setAnswer(ConfigureNodeFields.children_max.getFieldName(), max);
215    }
216
217    /**
218     * Gets the collection node which the node is affiliated with.
219     * 
220     * @return The collection node id
221     */
222    public String getCollection()
223    {
224        return getFieldValue(ConfigureNodeFields.collection);
225    }
226
227    /**
228     * Sets the collection node which the node is affiliated with.
229     * 
230     * @param collection The node id of the collection node
231     */
232    public void setCollection(String collection)
233    {
234        addField(ConfigureNodeFields.collection, FormField.Type.text_single);
235        setAnswer(ConfigureNodeFields.collection.getFieldName(), collection);
236    }
237
238    /**
239     * Gets the URL of an XSL transformation which can be applied to the payload
240     * format in order to generate a valid Data Forms result that the client could
241     * display using a generic Data Forms rendering engine.
242     * 
243     * @return The URL of an XSL transformation
244     */
245    public String getDataformXSLT()
246    {
247        return getFieldValue(ConfigureNodeFields.dataform_xslt);
248    }
249
250    /**
251     * Sets the URL of an XSL transformation which can be applied to the payload
252     * format in order to generate a valid Data Forms result that the client could
253     * display using a generic Data Forms rendering engine.
254     * 
255     * @param url The URL of an XSL transformation
256     */
257    public void setDataformXSLT(String url)
258    {
259        addField(ConfigureNodeFields.dataform_xslt, FormField.Type.text_single);
260        setAnswer(ConfigureNodeFields.dataform_xslt.getFieldName(), url);
261    }
262
263    /**
264     * Does the node deliver payloads with event notifications.
265     * 
266     * @return true if it does, false otherwise
267     */
268    public boolean isDeliverPayloads()
269    {
270        return parseBoolean(getFieldValue(ConfigureNodeFields.deliver_payloads));
271    }
272
273    /**
274     * Sets whether the node will deliver payloads with event notifications.
275     * 
276     * @param deliver true if the payload will be delivered, false otherwise
277     */
278    public void setDeliverPayloads(boolean deliver)
279    {
280        addField(ConfigureNodeFields.deliver_payloads, FormField.Type.bool);
281        setAnswer(ConfigureNodeFields.deliver_payloads.getFieldName(), deliver);
282    }
283
284    /**
285     * Determines who should get replies to items.
286     * 
287     * @return Who should get the reply
288     */
289    public ItemReply getItemReply()
290    {
291        String value = getFieldValue(ConfigureNodeFields.itemreply);
292
293        if (value == null)
294            return null;
295        else
296            return ItemReply.valueOf(value);
297    }
298
299    /**
300     * Sets who should get the replies to items.
301     * 
302     * @param reply Defines who should get the reply
303     */
304    public void setItemReply(ItemReply reply)
305    {
306        addField(ConfigureNodeFields.itemreply, FormField.Type.list_single);
307        setAnswer(ConfigureNodeFields.itemreply.getFieldName(), getListSingle(reply.toString()));
308    }
309
310    /**
311     * Gets the maximum number of items to persisted to this node if {@link #isPersistItems()} is
312     * true.
313     * 
314     * @return The maximum number of items to persist
315     */
316    public int getMaxItems()
317    {
318        return Integer.parseInt(getFieldValue(ConfigureNodeFields.max_items));
319    }
320
321    /**
322     * Set the maximum number of items to persisted to this node if {@link #isPersistItems()} is
323     * true.
324     * 
325     * @param max The maximum number of items to persist
326     */
327    public void setMaxItems(int max)
328    {
329        addField(ConfigureNodeFields.max_items, FormField.Type.text_single);
330        setAnswer(ConfigureNodeFields.max_items.getFieldName(), max);
331    }
332
333    /**
334     * Gets the maximum payload size in bytes.
335     * 
336     * @return The maximum payload size
337     */
338    public int getMaxPayloadSize()
339    {
340        return Integer.parseInt(getFieldValue(ConfigureNodeFields.max_payload_size));
341    }
342
343    /**
344     * Sets the maximum payload size in bytes.
345     * 
346     * @param max The maximum payload size
347     */
348    public void setMaxPayloadSize(int max)
349    {
350        addField(ConfigureNodeFields.max_payload_size, FormField.Type.text_single);
351        setAnswer(ConfigureNodeFields.max_payload_size.getFieldName(), max);
352    }
353
354    /**
355     * Gets the node type.
356     * 
357     * @return The node type
358     */
359    public NodeType getNodeType()
360    {
361        String value = getFieldValue(ConfigureNodeFields.node_type);
362
363        if (value == null)
364            return null;
365        else
366            return NodeType.valueOf(value);
367    }
368
369    /**
370     * Sets the node type.
371     * 
372     * @param type The node type
373     */
374    public void setNodeType(NodeType type)
375    {
376        addField(ConfigureNodeFields.node_type, FormField.Type.list_single);
377        setAnswer(ConfigureNodeFields.node_type.getFieldName(), getListSingle(type.toString()));
378    }
379
380    /**
381     * Determines if subscribers should be notified when the configuration changes.
382     * 
383     * @return true if they should be notified, false otherwise
384     */
385    public boolean isNotifyConfig()
386    {
387        return parseBoolean(getFieldValue(ConfigureNodeFields.notify_config));
388    }
389
390    /**
391     * Sets whether subscribers should be notified when the configuration changes.
392     * 
393     * @param notify true if subscribers should be notified, false otherwise
394     */
395    public void setNotifyConfig(boolean notify)
396    {
397        addField(ConfigureNodeFields.notify_config, FormField.Type.bool);
398        setAnswer(ConfigureNodeFields.notify_config.getFieldName(), notify);
399    }
400
401    /**
402     * Determines whether subscribers should be notified when the node is deleted.
403     * 
404     * @return true if subscribers should be notified, false otherwise
405     */
406    public boolean isNotifyDelete()
407    {
408        return parseBoolean(getFieldValue(ConfigureNodeFields.notify_delete));
409    }
410
411    /**
412     * Sets whether subscribers should be notified when the node is deleted.
413     * 
414     * @param notify true if subscribers should be notified, false otherwise
415     */
416    public void setNotifyDelete(boolean notify) 
417    {
418        addField(ConfigureNodeFields.notify_delete, FormField.Type.bool);
419        setAnswer(ConfigureNodeFields.notify_delete.getFieldName(), notify);
420    }
421
422    /**
423     * Determines whether subscribers should be notified when items are deleted 
424     * from the node.
425     * 
426     * @return true if subscribers should be notified, false otherwise
427     */
428    public boolean isNotifyRetract()
429    {
430        return parseBoolean(getFieldValue(ConfigureNodeFields.notify_retract));
431    }
432
433    /**
434     * Sets whether subscribers should be notified when items are deleted 
435     * from the node.
436     * 
437     * @param notify true if subscribers should be notified, false otherwise
438     */
439    public void setNotifyRetract(boolean notify) 
440    {
441        addField(ConfigureNodeFields.notify_retract, FormField.Type.bool);
442        setAnswer(ConfigureNodeFields.notify_retract.getFieldName(), notify);
443    }
444
445    /**
446     * Determines whether items should be persisted in the node.
447     * 
448     * @return true if items are persisted
449     */
450    public boolean isPersistItems()
451    {
452        return parseBoolean(getFieldValue(ConfigureNodeFields.persist_items));
453    }
454
455    /**
456     * Sets whether items should be persisted in the node.
457     * 
458     * @param persist true if items should be persisted, false otherwise
459     */
460    public void setPersistentItems(boolean persist) 
461    {
462        addField(ConfigureNodeFields.persist_items, FormField.Type.bool);
463        setAnswer(ConfigureNodeFields.persist_items.getFieldName(), persist);
464    }
465
466    /**
467     * Determines whether to deliver notifications to available users only.
468     * 
469     * @return true if users must be available
470     */
471    public boolean isPresenceBasedDelivery()
472    {
473        return parseBoolean(getFieldValue(ConfigureNodeFields.presence_based_delivery));
474    }
475
476    /**
477     * Sets whether to deliver notifications to available users only.
478     * 
479     * @param presenceBased true if user must be available, false otherwise
480     */
481    public void setPresenceBasedDelivery(boolean presenceBased) 
482    {
483        addField(ConfigureNodeFields.presence_based_delivery, FormField.Type.bool);
484        setAnswer(ConfigureNodeFields.presence_based_delivery.getFieldName(), presenceBased);
485    }
486
487    /**
488     * Gets the publishing model for the node, which determines who may publish to it.
489     * 
490     * @return The publishing model
491     */
492    public PublishModel getPublishModel()
493    {
494        String value = getFieldValue(ConfigureNodeFields.publish_model);
495
496        if (value == null)
497            return null;
498        else
499            return PublishModel.valueOf(value);
500    }
501
502    /**
503     * Sets the publishing model for the node, which determines who may publish to it.
504     * 
505     * @param publish The enum representing the possible options for the publishing model
506     */
507    public void setPublishModel(PublishModel publish) 
508    {
509        addField(ConfigureNodeFields.publish_model, FormField.Type.list_single);
510        setAnswer(ConfigureNodeFields.publish_model.getFieldName(), getListSingle(publish.toString()));
511    }
512
513    /**
514     * List of the multi user chat rooms that are specified as reply rooms.
515     * 
516     * @return The reply room JID's
517     */
518    public List<String> getReplyRoom()
519    {
520        return getFieldValues(ConfigureNodeFields.replyroom);
521    }
522
523    /**
524     * Sets the multi user chat rooms that are specified as reply rooms.
525     * 
526     * @param replyRooms The multi user chat room to use as reply rooms
527     */
528    public void setReplyRoom(List<String> replyRooms) 
529    {
530        addField(ConfigureNodeFields.replyroom, FormField.Type.list_multi);
531        setAnswer(ConfigureNodeFields.replyroom.getFieldName(), replyRooms);
532    }
533
534    /**
535     * Gets the specific JID's for reply to.
536     *  
537     * @return The JID's
538     */
539    public List<String> getReplyTo()
540    {
541        return getFieldValues(ConfigureNodeFields.replyto);
542    }
543
544    /**
545     * Sets the specific JID's for reply to.
546     * 
547     * @param replyTos The JID's to reply to
548     */
549    public void setReplyTo(List<String> replyTos)
550    {
551        addField(ConfigureNodeFields.replyto, FormField.Type.list_multi);
552        setAnswer(ConfigureNodeFields.replyto.getFieldName(), replyTos);
553    }
554
555    /**
556     * Gets the roster groups that are allowed to subscribe and retrieve items.
557     *  
558     * @return The roster groups
559     */
560    public List<String> getRosterGroupsAllowed()
561    {
562        return getFieldValues(ConfigureNodeFields.roster_groups_allowed);
563    }
564
565    /**
566     * Sets the roster groups that are allowed to subscribe and retrieve items.
567     * 
568     * @param groups The roster groups
569     */
570    public void setRosterGroupsAllowed(List<String> groups)
571    {
572        addField(ConfigureNodeFields.roster_groups_allowed, FormField.Type.list_multi);
573        setAnswer(ConfigureNodeFields.roster_groups_allowed.getFieldName(), groups);
574    }
575
576    /**
577     * Determines if subscriptions are allowed.
578     * 
579     * @return true if subscriptions are allowed, false otherwise
580     * @deprecated use {@link #isSubscribe()} instead
581     */
582    @Deprecated
583    public boolean isSubscibe()
584    {
585        return isSubscribe();
586    }
587
588    /**
589     * Determines if subscriptions are allowed.
590     * 
591     * @return true if subscriptions are allowed, false otherwise
592     */
593    public boolean isSubscribe() {
594        return parseBoolean(getFieldValue(ConfigureNodeFields.subscribe));
595    }
596
597    /**
598     * Sets whether subscriptions are allowed.
599     * 
600     * @param subscribe true if they are, false otherwise
601     */
602    public void setSubscribe(boolean subscribe)
603    {
604        addField(ConfigureNodeFields.subscribe, FormField.Type.bool);
605        setAnswer(ConfigureNodeFields.subscribe.getFieldName(), subscribe);
606    }
607
608    /**
609     * Gets the human readable node title.
610     * 
611     * @return The node title
612     */
613    @Override
614    public String getTitle()
615    {
616        return getFieldValue(ConfigureNodeFields.title);
617    }
618
619    /**
620     * Sets a human readable title for the node.
621     * 
622     * @param title The node title
623     */
624    @Override
625    public void setTitle(String title) 
626    {
627        addField(ConfigureNodeFields.title, FormField.Type.text_single);
628        setAnswer(ConfigureNodeFields.title.getFieldName(), title);
629    }
630
631    /**
632     * The type of node data, usually specified by the namespace of the payload (if any).
633     * 
634     * @return The type of node data
635     */
636    public String getDataType()
637    {
638        return getFieldValue(ConfigureNodeFields.type);
639    }
640
641    /**
642     * Sets the type of node data, usually specified by the namespace of the payload (if any).
643     * 
644     * @param type The type of node data
645     */
646    public void setDataType(String type) 
647    {
648        addField(ConfigureNodeFields.type, FormField.Type.text_single);
649        setAnswer(ConfigureNodeFields.type.getFieldName(), type);
650    }
651
652    @Override
653    public String toString()
654    {
655        StringBuilder result = new StringBuilder(getClass().getName() + " Content [");
656
657        for (FormField formField : getFields())
658        {
659            result.append('(');
660            result.append(formField.getVariable());
661            result.append(':');
662
663            StringBuilder valuesBuilder = new StringBuilder();
664
665            for (String value : formField.getValues())
666            {
667                if (valuesBuilder.length() > 0)
668                    result.append(',');
669                valuesBuilder.append(value);
670            }
671
672            if (valuesBuilder.length() == 0)
673                valuesBuilder.append("NOT SET");
674            result.append(valuesBuilder);
675            result.append(')');
676        }
677        result.append(']');
678        return result.toString();
679    }
680
681    static private boolean parseBoolean(String fieldValue)
682    {
683        return ("1".equals(fieldValue) || "true".equals(fieldValue));
684    }
685
686    private String getFieldValue(ConfigureNodeFields field)
687    {
688        FormField formField = getField(field.getFieldName());
689
690        return (formField.getValues().isEmpty()) ? null : formField.getValues().get(0);
691    }
692
693    private List<String> getFieldValues(ConfigureNodeFields field)
694    {
695        FormField formField = getField(field.getFieldName());
696
697        return formField.getValues();
698    }
699
700    private void addField(ConfigureNodeFields nodeField, FormField.Type type)
701    {
702        String fieldName = nodeField.getFieldName();
703
704        if (getField(fieldName) == null)
705        {
706            FormField field = new FormField(fieldName);
707            field.setType(type);
708            addField(field);
709        }
710    }
711
712    private static List<String> getListSingle(String value)
713    {
714        List<String> list = new ArrayList<>(1);
715        list.add(value);
716        return list;
717    }
718
719}