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(FormType formType)
069        {
070                super(formType.toString());
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<String>(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_BOOLEAN);
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_BOOLEAN);
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_BOOLEAN);
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_BOOLEAN);
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_BOOLEAN);
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_BOOLEAN);
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_BOOLEAN);
605                setAnswer(ConfigureNodeFields.subscribe.getFieldName(), subscribe);
606        }
607        
608        /**
609         * Gets the human readable node title.
610         * 
611         * @return The node title
612         */
613        public String getTitle()
614        {
615                return getFieldValue(ConfigureNodeFields.title);
616        }
617
618        /**
619         * Sets a human readable title for the node.
620         * 
621         * @param title The node title
622         */
623        public void setTitle(String title) 
624        {
625                addField(ConfigureNodeFields.title, FormField.TYPE_TEXT_SINGLE);
626                setAnswer(ConfigureNodeFields.title.getFieldName(), title);
627        }
628        
629        /**
630         * The type of node data, usually specified by the namespace of the payload (if any).
631         * 
632         * @return The type of node data
633         */
634        public String getDataType()
635        {
636                return getFieldValue(ConfigureNodeFields.type);
637        }
638
639        /**
640         * Sets the type of node data, usually specified by the namespace of the payload (if any).
641         * 
642         * @param type The type of node data
643         */
644        public void setDataType(String type) 
645        {
646                addField(ConfigureNodeFields.type, FormField.TYPE_TEXT_SINGLE);
647                setAnswer(ConfigureNodeFields.type.getFieldName(), type);
648        }
649        
650        @Override
651        public String toString()
652        {
653                StringBuilder result = new StringBuilder(getClass().getName() + " Content [");
654                
655                for (FormField formField : getFields())
656                {
657                        result.append('(');
658                        result.append(formField.getVariable());
659                        result.append(':');
660                        
661                        StringBuilder valuesBuilder = new StringBuilder();
662                                
663                        for (String value : formField.getValues())
664                        {
665                                if (valuesBuilder.length() > 0)
666                                        result.append(',');
667                                valuesBuilder.append(value);
668                        }
669                        
670                        if (valuesBuilder.length() == 0)
671                                valuesBuilder.append("NOT SET");
672                        result.append(valuesBuilder);
673                        result.append(')');
674                }
675                result.append(']');
676                return result.toString();
677        }
678
679        static private boolean parseBoolean(String fieldValue)
680        {
681                return ("1".equals(fieldValue) || "true".equals(fieldValue));
682        }
683
684        private String getFieldValue(ConfigureNodeFields field)
685        {
686                FormField formField = getField(field.getFieldName());
687                
688                return (formField.getValues().isEmpty()) ? null : formField.getValues().get(0);
689        }
690
691        private List<String> getFieldValues(ConfigureNodeFields field)
692        {
693                FormField formField = getField(field.getFieldName());
694                
695                return formField.getValues();
696        }
697
698        private void addField(ConfigureNodeFields nodeField, String type)
699        {
700                String fieldName = nodeField.getFieldName();
701                
702                if (getField(fieldName) == null)
703                {
704                        FormField field = new FormField(fieldName);
705                        field.setType(type);
706                        addField(field);
707                }
708        }
709
710        private List<String> getListSingle(String value)
711        {
712                List<String> list = new ArrayList<String>(1);
713                list.add(value);
714                return list;
715        }
716
717}