ConfigureForm.java

  1. /**
  2.  *
  3.  * Copyright the original author or authors
  4.  *
  5.  * Licensed under the Apache License, Version 2.0 (the "License");
  6.  * you may not use this file except in compliance with the License.
  7.  * You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.jivesoftware.smackx.pubsub;

  18. import java.util.ArrayList;
  19. import java.util.List;

  20. import org.jivesoftware.smackx.xdata.Form;
  21. import org.jivesoftware.smackx.xdata.FormField;
  22. import org.jivesoftware.smackx.xdata.packet.DataForm;

  23. /**
  24.  * A decorator for a {@link Form} to easily enable reading and updating
  25.  * of node configuration.  All operations read or update the underlying {@link DataForm}.
  26.  *
  27.  * <p>Unlike the {@link Form}.setAnswer(XXX)} methods, which throw an exception if the field does not
  28.  * exist, all <b>ConfigureForm.setXXX</b> methods will create the field in the wrapped form
  29.  * if it does not already exist.
  30.  *
  31.  * @author Robin Collier
  32.  */
  33. public class ConfigureForm extends Form
  34. {
  35.     /**
  36.      * Create a decorator from an existing {@link DataForm} that has been
  37.      * retrieved from parsing a node configuration request.
  38.      *
  39.      * @param configDataForm
  40.      */
  41.     public ConfigureForm(DataForm configDataForm)
  42.     {
  43.         super(configDataForm);
  44.     }
  45.    
  46.     /**
  47.      * Create a decorator from an existing {@link Form} for node configuration.
  48.      * Typically, this can be used to create a decorator for an answer form
  49.      * by using the result of {@link #createAnswerForm()} as the input parameter.
  50.      *
  51.      * @param nodeConfigForm
  52.      */
  53.     public ConfigureForm(Form nodeConfigForm)
  54.     {
  55.         super(nodeConfigForm.getDataFormToSend());
  56.     }
  57.    
  58.     /**
  59.      * Create a new form for configuring a node.  This would typically only be used
  60.      * when creating and configuring a node at the same time via {@link PubSubManager#createNode(String, Form)}, since
  61.      * configuration of an existing node is typically accomplished by calling {@link LeafNode#getNodeConfiguration()} and
  62.      * using the resulting form to create a answer form.  See {@link #ConfigureForm(Form)}.
  63.      * @param formType
  64.      */
  65.     public ConfigureForm(DataForm.Type formType)
  66.     {
  67.         super(formType);
  68.     }
  69.    
  70.     /**
  71.      * Get the currently configured {@link AccessModel}, null if it is not set.
  72.      *
  73.      * @return The current {@link AccessModel}
  74.      */
  75.     public AccessModel getAccessModel()
  76.     {
  77.         String value = getFieldValue(ConfigureNodeFields.access_model);
  78.        
  79.         if (value == null)
  80.             return null;
  81.         else
  82.             return AccessModel.valueOf(value);
  83.     }
  84.    
  85.     /**
  86.      * Sets the value of access model.
  87.      *
  88.      * @param accessModel
  89.      */
  90.     public void setAccessModel(AccessModel accessModel)
  91.     {
  92.         addField(ConfigureNodeFields.access_model, FormField.Type.list_single);
  93.         setAnswer(ConfigureNodeFields.access_model.getFieldName(), getListSingle(accessModel.toString()));
  94.     }

  95.     /**
  96.      * Returns the URL of an XSL transformation which can be applied to payloads in order to
  97.      * generate an appropriate message body element.
  98.      *
  99.      * @return URL to an XSL
  100.      */
  101.     public String getBodyXSLT()
  102.     {
  103.         return getFieldValue(ConfigureNodeFields.body_xslt);
  104.     }

  105.     /**
  106.      * Set the URL of an XSL transformation which can be applied to payloads in order to
  107.      * generate an appropriate message body element.
  108.      *
  109.      * @param bodyXslt The URL of an XSL
  110.      */
  111.     public void setBodyXSLT(String bodyXslt)
  112.     {
  113.         addField(ConfigureNodeFields.body_xslt, FormField.Type.text_single);
  114.         setAnswer(ConfigureNodeFields.body_xslt.getFieldName(), bodyXslt);
  115.     }
  116.    
  117.     /**
  118.      * The id's of the child nodes associated with a collection node (both leaf and collection).
  119.      *
  120.      * @return list of child nodes.
  121.      */
  122.     public List<String> getChildren()
  123.     {
  124.         return getFieldValues(ConfigureNodeFields.children);
  125.     }
  126.    
  127.     /**
  128.      * Set the list of child node ids that are associated with a collection node.
  129.      *
  130.      * @param children
  131.      */
  132.     public void setChildren(List<String> children)
  133.     {
  134.         addField(ConfigureNodeFields.children, FormField.Type.text_multi);
  135.         setAnswer(ConfigureNodeFields.children.getFieldName(), children);
  136.     }
  137.    
  138.     /**
  139.      * Returns the policy that determines who may associate children with the node.
  140.      *  
  141.      * @return The current policy
  142.      */
  143.     public ChildrenAssociationPolicy getChildrenAssociationPolicy()
  144.     {
  145.         String value = getFieldValue(ConfigureNodeFields.children_association_policy);
  146.        
  147.         if (value == null)
  148.             return null;
  149.         else
  150.             return ChildrenAssociationPolicy.valueOf(value);
  151.     }
  152.    
  153.     /**
  154.      * Sets the policy that determines who may associate children with the node.
  155.      *
  156.      * @param policy The policy being set
  157.      */
  158.     public void setChildrenAssociationPolicy(ChildrenAssociationPolicy policy)
  159.     {
  160.         addField(ConfigureNodeFields.children_association_policy, FormField.Type.list_single);
  161.         List<String> values = new ArrayList<String>(1);
  162.         values.add(policy.toString());
  163.         setAnswer(ConfigureNodeFields.children_association_policy.getFieldName(), values);
  164.     }
  165.    
  166.     /**
  167.      * List of JID's that are on the whitelist that determines who can associate child nodes
  168.      * with the collection node.  This is only relevant if {@link #getChildrenAssociationPolicy()} is set to
  169.      * {@link ChildrenAssociationPolicy#whitelist}.
  170.      *
  171.      * @return List of the whitelist
  172.      */
  173.     public List<String> getChildrenAssociationWhitelist()
  174.     {
  175.         return getFieldValues(ConfigureNodeFields.children_association_whitelist);
  176.     }
  177.    
  178.     /**
  179.      * Set the JID's in the whitelist of users that can associate child nodes with the collection
  180.      * node.  This is only relevant if {@link #getChildrenAssociationPolicy()} is set to
  181.      * {@link ChildrenAssociationPolicy#whitelist}.
  182.      *
  183.      * @param whitelist The list of JID's
  184.      */
  185.     public void setChildrenAssociationWhitelist(List<String> whitelist)
  186.     {
  187.         addField(ConfigureNodeFields.children_association_whitelist, FormField.Type.jid_multi);
  188.         setAnswer(ConfigureNodeFields.children_association_whitelist.getFieldName(), whitelist);
  189.     }

  190.     /**
  191.      * Gets the maximum number of child nodes that can be associated with the collection node.
  192.      *
  193.      * @return The maximum number of child nodes
  194.      */
  195.     public int getChildrenMax()
  196.     {
  197.         return Integer.parseInt(getFieldValue(ConfigureNodeFields.children_max));
  198.     }

  199.     /**
  200.      * Set the maximum number of child nodes that can be associated with a collection node.
  201.      *
  202.      * @param max The maximum number of child nodes.
  203.      */
  204.     public void setChildrenMax(int max)
  205.     {
  206.         addField(ConfigureNodeFields.children_max, FormField.Type.text_single);
  207.         setAnswer(ConfigureNodeFields.children_max.getFieldName(), max);
  208.     }

  209.     /**
  210.      * Gets the collection node which the node is affiliated with.
  211.      *
  212.      * @return The collection node id
  213.      */
  214.     public String getCollection()
  215.     {
  216.         return getFieldValue(ConfigureNodeFields.collection);
  217.     }

  218.     /**
  219.      * Sets the collection node which the node is affiliated with.
  220.      *
  221.      * @param collection The node id of the collection node
  222.      */
  223.     public void setCollection(String collection)
  224.     {
  225.         addField(ConfigureNodeFields.collection, FormField.Type.text_single);
  226.         setAnswer(ConfigureNodeFields.collection.getFieldName(), collection);
  227.     }

  228.     /**
  229.      * Gets the URL of an XSL transformation which can be applied to the payload
  230.      * format in order to generate a valid Data Forms result that the client could
  231.      * display using a generic Data Forms rendering engine.
  232.      *
  233.      * @return The URL of an XSL transformation
  234.      */
  235.     public String getDataformXSLT()
  236.     {
  237.         return getFieldValue(ConfigureNodeFields.dataform_xslt);
  238.     }

  239.     /**
  240.      * Sets the URL of an XSL transformation which can be applied to the payload
  241.      * format in order to generate a valid Data Forms result that the client could
  242.      * display using a generic Data Forms rendering engine.
  243.      *
  244.      * @param url The URL of an XSL transformation
  245.      */
  246.     public void setDataformXSLT(String url)
  247.     {
  248.         addField(ConfigureNodeFields.dataform_xslt, FormField.Type.text_single);
  249.         setAnswer(ConfigureNodeFields.dataform_xslt.getFieldName(), url);
  250.     }

  251.     /**
  252.      * Does the node deliver payloads with event notifications.
  253.      *
  254.      * @return true if it does, false otherwise
  255.      */
  256.     public boolean isDeliverPayloads()
  257.     {
  258.         return parseBoolean(getFieldValue(ConfigureNodeFields.deliver_payloads));
  259.     }
  260.    
  261.     /**
  262.      * Sets whether the node will deliver payloads with event notifications.
  263.      *
  264.      * @param deliver true if the payload will be delivered, false otherwise
  265.      */
  266.     public void setDeliverPayloads(boolean deliver)
  267.     {
  268.         addField(ConfigureNodeFields.deliver_payloads, FormField.Type.bool);
  269.         setAnswer(ConfigureNodeFields.deliver_payloads.getFieldName(), deliver);
  270.     }

  271.     /**
  272.      * Determines who should get replies to items
  273.      *
  274.      * @return Who should get the reply
  275.      */
  276.     public ItemReply getItemReply()
  277.     {
  278.         String value = getFieldValue(ConfigureNodeFields.itemreply);
  279.        
  280.         if (value == null)
  281.             return null;
  282.         else
  283.             return ItemReply.valueOf(value);
  284.     }

  285.     /**
  286.      * Sets who should get the replies to items
  287.      *
  288.      * @param reply Defines who should get the reply
  289.      */
  290.     public void setItemReply(ItemReply reply)
  291.     {
  292.         addField(ConfigureNodeFields.itemreply, FormField.Type.list_single);
  293.         setAnswer(ConfigureNodeFields.itemreply.getFieldName(), getListSingle(reply.toString()));
  294.     }

  295.     /**
  296.      * Gets the maximum number of items to persisted to this node if {@link #isPersistItems()} is
  297.      * true.
  298.      *
  299.      * @return The maximum number of items to persist
  300.      */
  301.     public int getMaxItems()
  302.     {
  303.         return Integer.parseInt(getFieldValue(ConfigureNodeFields.max_items));
  304.     }

  305.     /**
  306.      * Set the maximum number of items to persisted to this node if {@link #isPersistItems()} is
  307.      * true.
  308.      *
  309.      * @param max The maximum number of items to persist
  310.      */
  311.     public void setMaxItems(int max)
  312.     {
  313.         addField(ConfigureNodeFields.max_items, FormField.Type.text_single);
  314.         setAnswer(ConfigureNodeFields.max_items.getFieldName(), max);
  315.     }
  316.    
  317.     /**
  318.      * Gets the maximum payload size in bytes.
  319.      *
  320.      * @return The maximum payload size
  321.      */
  322.     public int getMaxPayloadSize()
  323.     {
  324.         return Integer.parseInt(getFieldValue(ConfigureNodeFields.max_payload_size));
  325.     }

  326.     /**
  327.      * Sets the maximum payload size in bytes
  328.      *
  329.      * @param max The maximum payload size
  330.      */
  331.     public void setMaxPayloadSize(int max)
  332.     {
  333.         addField(ConfigureNodeFields.max_payload_size, FormField.Type.text_single);
  334.         setAnswer(ConfigureNodeFields.max_payload_size.getFieldName(), max);
  335.     }
  336.    
  337.     /**
  338.      * Gets the node type
  339.      *
  340.      * @return The node type
  341.      */
  342.     public NodeType getNodeType()
  343.     {
  344.         String value = getFieldValue(ConfigureNodeFields.node_type);
  345.        
  346.         if (value == null)
  347.             return null;
  348.         else
  349.             return NodeType.valueOf(value);
  350.     }
  351.    
  352.     /**
  353.      * Sets the node type
  354.      *
  355.      * @param type The node type
  356.      */
  357.     public void setNodeType(NodeType type)
  358.     {
  359.         addField(ConfigureNodeFields.node_type, FormField.Type.list_single);
  360.         setAnswer(ConfigureNodeFields.node_type.getFieldName(), getListSingle(type.toString()));
  361.     }

  362.     /**
  363.      * Determines if subscribers should be notified when the configuration changes.
  364.      *
  365.      * @return true if they should be notified, false otherwise
  366.      */
  367.     public boolean isNotifyConfig()
  368.     {
  369.         return parseBoolean(getFieldValue(ConfigureNodeFields.notify_config));
  370.     }
  371.    
  372.     /**
  373.      * Sets whether subscribers should be notified when the configuration changes.
  374.      *
  375.      * @param notify true if subscribers should be notified, false otherwise
  376.      */
  377.     public void setNotifyConfig(boolean notify)
  378.     {
  379.         addField(ConfigureNodeFields.notify_config, FormField.Type.bool);
  380.         setAnswer(ConfigureNodeFields.notify_config.getFieldName(), notify);
  381.     }

  382.     /**
  383.      * Determines whether subscribers should be notified when the node is deleted.
  384.      *
  385.      * @return true if subscribers should be notified, false otherwise
  386.      */
  387.     public boolean isNotifyDelete()
  388.     {
  389.         return parseBoolean(getFieldValue(ConfigureNodeFields.notify_delete));
  390.     }
  391.    
  392.     /**
  393.      * Sets whether subscribers should be notified when the node is deleted.
  394.      *
  395.      * @param notify true if subscribers should be notified, false otherwise
  396.      */
  397.     public void setNotifyDelete(boolean notify)
  398.     {
  399.         addField(ConfigureNodeFields.notify_delete, FormField.Type.bool);
  400.         setAnswer(ConfigureNodeFields.notify_delete.getFieldName(), notify);
  401.     }

  402.     /**
  403.      * Determines whether subscribers should be notified when items are deleted
  404.      * from the node.
  405.      *
  406.      * @return true if subscribers should be notified, false otherwise
  407.      */
  408.     public boolean isNotifyRetract()
  409.     {
  410.         return parseBoolean(getFieldValue(ConfigureNodeFields.notify_retract));
  411.     }
  412.    
  413.     /**
  414.      * Sets whether subscribers should be notified when items are deleted
  415.      * from the node.
  416.      *
  417.      * @param notify true if subscribers should be notified, false otherwise
  418.      */
  419.     public void setNotifyRetract(boolean notify)
  420.     {
  421.         addField(ConfigureNodeFields.notify_retract, FormField.Type.bool);
  422.         setAnswer(ConfigureNodeFields.notify_retract.getFieldName(), notify);
  423.     }
  424.    
  425.     /**
  426.      * Determines whether items should be persisted in the node.
  427.      *
  428.      * @return true if items are persisted
  429.      */
  430.     public boolean isPersistItems()
  431.     {
  432.         return parseBoolean(getFieldValue(ConfigureNodeFields.persist_items));
  433.     }
  434.    
  435.     /**
  436.      * Sets whether items should be persisted in the node.
  437.      *
  438.      * @param persist true if items should be persisted, false otherwise
  439.      */
  440.     public void setPersistentItems(boolean persist)
  441.     {
  442.         addField(ConfigureNodeFields.persist_items, FormField.Type.bool);
  443.         setAnswer(ConfigureNodeFields.persist_items.getFieldName(), persist);
  444.     }

  445.     /**
  446.      * Determines whether to deliver notifications to available users only.
  447.      *
  448.      * @return true if users must be available
  449.      */
  450.     public boolean isPresenceBasedDelivery()
  451.     {
  452.         return parseBoolean(getFieldValue(ConfigureNodeFields.presence_based_delivery));
  453.     }
  454.    
  455.     /**
  456.      * Sets whether to deliver notifications to available users only.
  457.      *
  458.      * @param presenceBased true if user must be available, false otherwise
  459.      */
  460.     public void setPresenceBasedDelivery(boolean presenceBased)
  461.     {
  462.         addField(ConfigureNodeFields.presence_based_delivery, FormField.Type.bool);
  463.         setAnswer(ConfigureNodeFields.presence_based_delivery.getFieldName(), presenceBased);
  464.     }

  465.     /**
  466.      * Gets the publishing model for the node, which determines who may publish to it.
  467.      *
  468.      * @return The publishing model
  469.      */
  470.     public PublishModel getPublishModel()
  471.     {
  472.         String value = getFieldValue(ConfigureNodeFields.publish_model);
  473.        
  474.         if (value == null)
  475.             return null;
  476.         else
  477.             return PublishModel.valueOf(value);
  478.     }

  479.     /**
  480.      * Sets the publishing model for the node, which determines who may publish to it.
  481.      *
  482.      * @param publish The enum representing the possible options for the publishing model
  483.      */
  484.     public void setPublishModel(PublishModel publish)
  485.     {
  486.         addField(ConfigureNodeFields.publish_model, FormField.Type.list_single);
  487.         setAnswer(ConfigureNodeFields.publish_model.getFieldName(), getListSingle(publish.toString()));
  488.     }
  489.    
  490.     /**
  491.      * List of the multi user chat rooms that are specified as reply rooms.
  492.      *
  493.      * @return The reply room JID's
  494.      */
  495.     public List<String> getReplyRoom()
  496.     {
  497.         return getFieldValues(ConfigureNodeFields.replyroom);
  498.     }
  499.    
  500.     /**
  501.      * Sets the multi user chat rooms that are specified as reply rooms.
  502.      *
  503.      * @param replyRooms The multi user chat room to use as reply rooms
  504.      */
  505.     public void setReplyRoom(List<String> replyRooms)
  506.     {
  507.         addField(ConfigureNodeFields.replyroom, FormField.Type.list_multi);
  508.         setAnswer(ConfigureNodeFields.replyroom.getFieldName(), replyRooms);
  509.     }
  510.    
  511.     /**
  512.      * Gets the specific JID's for reply to.
  513.      *  
  514.      * @return The JID's
  515.      */
  516.     public List<String> getReplyTo()
  517.     {
  518.         return getFieldValues(ConfigureNodeFields.replyto);
  519.     }
  520.    
  521.     /**
  522.      * Sets the specific JID's for reply to.
  523.      *
  524.      * @param replyTos The JID's to reply to
  525.      */
  526.     public void setReplyTo(List<String> replyTos)
  527.     {
  528.         addField(ConfigureNodeFields.replyto, FormField.Type.list_multi);
  529.         setAnswer(ConfigureNodeFields.replyto.getFieldName(), replyTos);
  530.     }
  531.    
  532.     /**
  533.      * Gets the roster groups that are allowed to subscribe and retrieve items.
  534.      *  
  535.      * @return The roster groups
  536.      */
  537.     public List<String> getRosterGroupsAllowed()
  538.     {
  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.     {
  549.         addField(ConfigureNodeFields.roster_groups_allowed, FormField.Type.list_multi);
  550.         setAnswer(ConfigureNodeFields.roster_groups_allowed.getFieldName(), groups);
  551.     }
  552.    
  553.     /**
  554.      * Determines if subscriptions are allowed.
  555.      *
  556.      * @return true if subscriptions are allowed, false otherwise
  557.      * @deprecated use {@link #isSubscribe()} instead
  558.      */
  559.     @Deprecated
  560.     public boolean isSubscibe()
  561.     {
  562.         return isSubscribe();
  563.     }

  564.     /**
  565.      * Determines if subscriptions are allowed.
  566.      *
  567.      * @return true if subscriptions are allowed, false otherwise
  568.      */
  569.     public boolean isSubscribe() {
  570.         return parseBoolean(getFieldValue(ConfigureNodeFields.subscribe));
  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.     {
  579.         addField(ConfigureNodeFields.subscribe, FormField.Type.bool);
  580.         setAnswer(ConfigureNodeFields.subscribe.getFieldName(), subscribe);
  581.     }
  582.    
  583.     /**
  584.      * Gets the human readable node title.
  585.      *
  586.      * @return The node title
  587.      */
  588.     public String getTitle()
  589.     {
  590.         return getFieldValue(ConfigureNodeFields.title);
  591.     }

  592.     /**
  593.      * Sets a human readable title for the node.
  594.      *
  595.      * @param title The node title
  596.      */
  597.     public void setTitle(String title)
  598.     {
  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.     {
  610.         return getFieldValue(ConfigureNodeFields.type);
  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.     {
  619.         addField(ConfigureNodeFields.type, FormField.Type.text_single);
  620.         setAnswer(ConfigureNodeFields.type.getFieldName(), type);
  621.     }
  622.    
  623.     @Override
  624.     public String toString()
  625.     {
  626.         StringBuilder result = new StringBuilder(getClass().getName() + " Content [");
  627.        
  628.         for (FormField formField : getFields())
  629.         {
  630.             result.append('(');
  631.             result.append(formField.getVariable());
  632.             result.append(':');
  633.            
  634.             StringBuilder valuesBuilder = new StringBuilder();
  635.                
  636.             for (String value : formField.getValues())
  637.             {
  638.                 if (valuesBuilder.length() > 0)
  639.                     result.append(',');
  640.                 valuesBuilder.append(value);
  641.             }
  642.            
  643.             if (valuesBuilder.length() == 0)
  644.                 valuesBuilder.append("NOT SET");
  645.             result.append(valuesBuilder);
  646.             result.append(')');
  647.         }
  648.         result.append(']');
  649.         return result.toString();
  650.     }

  651.     static private boolean parseBoolean(String fieldValue)
  652.     {
  653.         return ("1".equals(fieldValue) || "true".equals(fieldValue));
  654.     }

  655.     private String getFieldValue(ConfigureNodeFields field)
  656.     {
  657.         FormField formField = getField(field.getFieldName());
  658.        
  659.         return (formField.getValues().isEmpty()) ? null : formField.getValues().get(0);
  660.     }

  661.     private List<String> getFieldValues(ConfigureNodeFields field)
  662.     {
  663.         FormField formField = getField(field.getFieldName());
  664.        
  665.         return formField.getValues();
  666.     }

  667.     private void addField(ConfigureNodeFields nodeField, FormField.Type type)
  668.     {
  669.         String fieldName = nodeField.getFieldName();
  670.        
  671.         if (getField(fieldName) == null)
  672.         {
  673.             FormField field = new FormField(fieldName);
  674.             field.setType(type);
  675.             addField(field);
  676.         }
  677.     }

  678.     private List<String> getListSingle(String value)
  679.     {
  680.         List<String> list = new ArrayList<String>(1);
  681.         list.add(value);
  682.         return list;
  683.     }

  684. }