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