001package com.box.sdk;
002
003import java.net.URL;
004import java.util.ArrayList;
005import java.util.List;
006
007import com.eclipsesource.json.JsonArray;
008import com.eclipsesource.json.JsonObject;
009import com.eclipsesource.json.JsonValue;
010
011/**
012 * The MetadataTemplate class represents the Box metadata template object.
013 * Templates allow the metadata service to provide a multitude of services,
014 * such as pre-defining sets of key:value pairs or schema enforcement on specific fields.
015 *
016 * @see <a href="https://docs.box.com/reference#metadata-templates">Box metadata templates</a>
017 */
018public class MetadataTemplate extends BoxJSONObject {
019
020    /**
021     * @see #getMetadataTemplate(BoxAPIConnection)
022     */
023    public static final URLTemplate METADATA_TEMPLATE_URL_TEMPLATE
024        = new URLTemplate("metadata_templates/%s/%s/schema");
025
026    /**
027     * @see #getMetadataTemplateByID(BoxAPIConnection, String)
028     */
029    public static final URLTemplate METADATA_TEMPLATE_BY_ID_URL_TEMPLATE = new URLTemplate("metadata_templates/%s");
030
031    /**
032     * @see #createMetadataTemplate(BoxAPIConnection, String, String, String, boolean, List)
033     */
034    public static final URLTemplate METADATA_TEMPLATE_SCHEMA_URL_TEMPLATE
035        = new URLTemplate("metadata_templates/schema");
036
037    /**
038     * @see #getEnterpriseMetadataTemplates(String, int, BoxAPIConnection, String...)
039     */
040    public static final URLTemplate ENTERPRISE_METADATA_URL_TEMPLATE = new URLTemplate("metadata_templates/%s");
041
042    /**
043     *
044     */
045    private static final URLTemplate METADATA_QUERIES_URL_TEMPLATE = new URLTemplate("metadata_queries/execute_read");
046
047    /**
048     * Default metadata type to be used in query.
049     */
050    private static final String DEFAULT_METADATA_TYPE = "properties";
051
052    /**
053     * Global metadata scope. Used by default if the metadata type is "properties".
054     */
055    private static final String GLOBAL_METADATA_SCOPE = "global";
056
057    /**
058     * Enterprise metadata scope. Used by default if the metadata type is not "properties".
059     */
060    private static final String ENTERPRISE_METADATA_SCOPE = "enterprise";
061
062    /**
063     * Default number of entries per page.
064     */
065    private static final int DEFAULT_ENTRIES_LIMIT = 100;
066
067    /**
068     * @see #getID()
069     */
070    private String id;
071
072    /**
073     * @see #getTemplateKey()
074     */
075    private String templateKey;
076
077    /**
078     * @see #getScope()
079     */
080    private String scope;
081
082    /**
083     * @see #getDisplayName()
084     */
085    private String displayName;
086
087    /**
088     * @see #getIsHidden()
089     */
090    private Boolean isHidden;
091
092    /**
093     * @see #getFields()
094     */
095    private List<Field> fields;
096
097    /**
098     * Constructs an empty metadata template.
099     */
100    public MetadataTemplate() {
101        super();
102    }
103
104    /**
105     * Constructs a metadata template from a JSON string.
106     * @param json the json encoded metadate template.
107     */
108    public MetadataTemplate(String json) {
109        super(json);
110    }
111
112    /**
113     * Constructs a metadate template from a JSON object.
114     * @param jsonObject the json encoded metadate template.
115     */
116    MetadataTemplate(JsonObject jsonObject) {
117        super(jsonObject);
118    }
119
120    /**
121     * Gets the ID of the template.
122     * @return the template ID.
123     */
124    public String getID() {
125        return this.id;
126    }
127
128    /**
129     * Gets the unique template key to identify the metadata template.
130     * @return the unique template key to identify the metadata template.
131     */
132    public String getTemplateKey() {
133        return this.templateKey;
134    }
135
136    /**
137     * Gets the metadata template scope.
138     * @return the metadata template scope.
139     */
140    public String getScope() {
141        return this.scope;
142    }
143
144    /**
145     * Gets the displayed metadata template name.
146     * @return the displayed metadata template name.
147     */
148    public String getDisplayName() {
149        return this.displayName;
150    }
151
152    /**
153     * Gets is the metadata template hidden.
154     * @return is the metadata template hidden.
155     */
156    public Boolean getIsHidden() {
157        return this.isHidden;
158    }
159
160    /**
161     * Gets the iterable with all fields the metadata template contains.
162     * @return the iterable with all fields the metadata template contains.
163     */
164    public List<Field> getFields() {
165        return this.fields;
166    }
167
168    /**
169     * {@inheritDoc}
170     */
171    @Override
172    void parseJSONMember(JsonObject.Member member) {
173        JsonValue value = member.getValue();
174        String memberName = member.getName();
175        if (memberName.equals("templateKey")) {
176            this.templateKey = value.asString();
177        } else if (memberName.equals("scope")) {
178            this.scope = value.asString();
179        } else if (memberName.equals("displayName")) {
180            this.displayName = value.asString();
181        } else if (memberName.equals("hidden")) {
182            this.isHidden = value.asBoolean();
183        } else if (memberName.equals("fields")) {
184            this.fields = new ArrayList<Field>();
185            for (JsonValue field: value.asArray()) {
186                this.fields.add(new Field(field.asObject()));
187            }
188        } else if (memberName.equals("id")) {
189            this.id = value.asString();
190        }
191    }
192
193    /**
194     * Creates new metadata template.
195     * @param api the API connection to be used.
196     * @param scope the scope of the object.
197     * @param templateKey a unique identifier for the template.
198     * @param displayName the display name of the field.
199     * @param hidden whether this template is hidden in the UI.
200     * @param fields the ordered set of fields for the template
201     * @return the metadata template returned from the server.
202     */
203    public static MetadataTemplate createMetadataTemplate(BoxAPIConnection api, String scope, String templateKey,
204            String displayName, boolean hidden, List<Field> fields) {
205
206        JsonObject jsonObject = new JsonObject();
207        jsonObject.add("scope", scope);
208        jsonObject.add("displayName", displayName);
209        jsonObject.add("hidden", hidden);
210
211        if (templateKey != null) {
212            jsonObject.add("templateKey", templateKey);
213        }
214
215        JsonArray fieldsArray = new JsonArray();
216        if (fields != null && !fields.isEmpty()) {
217            for (Field field : fields) {
218                JsonObject fieldObj = getFieldJsonObject(field);
219
220                fieldsArray.add(fieldObj);
221            }
222
223            jsonObject.add("fields", fieldsArray);
224        }
225
226        URL url = METADATA_TEMPLATE_SCHEMA_URL_TEMPLATE.build(api.getBaseURL());
227        BoxJSONRequest request = new BoxJSONRequest(api, url, "POST");
228        request.setBody(jsonObject.toString());
229
230        BoxJSONResponse response = (BoxJSONResponse) request.send();
231        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
232
233        return new MetadataTemplate(responseJSON);
234    }
235
236    /**
237     * Gets the JsonObject representation of the given field object.
238     * @param field represents a template field
239     * @return the json object
240     */
241    private static JsonObject getFieldJsonObject(Field field) {
242        JsonObject fieldObj = new JsonObject();
243        fieldObj.add("type", field.getType());
244        fieldObj.add("key", field.getKey());
245        fieldObj.add("displayName", field.getDisplayName());
246
247        String fieldDesc = field.getDescription();
248        if (fieldDesc != null) {
249            fieldObj.add("description", field.getDescription());
250        }
251
252        Boolean fieldIsHidden = field.getIsHidden();
253        if (fieldIsHidden != null) {
254            fieldObj.add("hidden", field.getIsHidden());
255        }
256
257        JsonArray array = new JsonArray();
258        List<String> options = field.getOptions();
259        if (options != null && !options.isEmpty()) {
260            for (String option : options) {
261                JsonObject optionObj = new JsonObject();
262                optionObj.add("key", option);
263
264                array.add(optionObj);
265            }
266            fieldObj.add("options", array);
267        }
268
269        return fieldObj;
270    }
271
272    /**
273     * Updates the schema of an existing metadata template.
274     *
275     * @param api the API connection to be used
276     * @param scope the scope of the object
277     * @param template Unique identifier of the template
278     * @param fieldOperations the fields that needs to be updated / added in the template
279     * @return the updated metadata template
280     */
281    public static MetadataTemplate updateMetadataTemplate(BoxAPIConnection api, String scope, String template,
282            List<FieldOperation> fieldOperations) {
283
284        JsonArray array = new JsonArray();
285
286        for (FieldOperation fieldOperation : fieldOperations) {
287            JsonObject jsonObject = getFieldOperationJsonObject(fieldOperation);
288            array.add(jsonObject);
289        }
290
291        QueryStringBuilder builder = new QueryStringBuilder();
292        URL url = METADATA_TEMPLATE_URL_TEMPLATE.build(api.getBaseURL(), scope, template);
293        BoxJSONRequest request = new BoxJSONRequest(api, url, "PUT");
294        request.setBody(array.toString());
295
296        BoxJSONResponse response = (BoxJSONResponse) request.send();
297        JsonObject responseJson = JsonObject.readFrom(response.getJSON());
298
299        return new MetadataTemplate(responseJson);
300    }
301
302    /**
303     * Deletes the schema of an existing metadata template.
304     *
305     * @param api the API connection to be used
306     * @param scope the scope of the object
307     * @param template Unique identifier of the template
308     */
309    public static void deleteMetadataTemplate(BoxAPIConnection api, String scope, String template) {
310
311        URL url = METADATA_TEMPLATE_URL_TEMPLATE.build(api.getBaseURL(), scope, template);
312        BoxJSONRequest request = new BoxJSONRequest(api, url, "DELETE");
313
314        request.send();
315    }
316
317    /**
318     * Executes a metadata query.
319     *
320     * @param api the API connection to be used
321     * @param from Required: the template used in the query. Must be in the form scope.templateKey
322     * @param query Optional: the logical expression of the query
323     * @param queryParameters Optional: Required if query present. the arguments for the query
324     * @param ancestorFolderId Optional: the folder_id to which to restrain the query
325     * @param indexName Optional: the name of the Index to use
326     * @param orderBy Optional: the field_key(s) to order on and the corresponding direction(s)
327     * @param limit Optional: max results to return for a single request (0-100 inclusive)
328     * @param marker Optional: the marker to use for requesting the next page
329     * @return An iterable of BoxItem.Info search results
330     */
331    public static BoxResourceIterable<BoxItem.Info> executeMetadataQuery(final BoxAPIConnection api,
332                                            String from, String query, JsonObject queryParameters,
333                                            String ancestorFolderId, String indexName,
334                                            JsonArray orderBy, int limit, String marker) {
335
336        JsonObject jsonObject = new JsonObject().add("from", from);
337        if (query != null) {
338            jsonObject.add("query", query);
339        }
340        if (queryParameters != null) {
341            jsonObject.add("query_params", queryParameters);
342        }
343        if (ancestorFolderId != null) {
344            jsonObject.add("ancestor_folder_id", ancestorFolderId);
345        }
346        if (indexName != null) {
347            jsonObject.add("use_index", indexName);
348        }
349        if (orderBy != null) {
350            jsonObject.add("order_by", orderBy);
351        }
352        jsonObject.add("limit", limit);
353        if (marker != null) {
354            jsonObject.add("marker", marker);
355        }
356
357        URL url = METADATA_QUERIES_URL_TEMPLATE.build(api.getBaseURL());
358        return new BoxResourceIterable<BoxItem.Info>(api, url, limit, jsonObject, marker) {
359
360            @Override
361            protected BoxItem.Info factory(JsonObject jsonObject) {
362                if (jsonObject != null) {
363                    JsonObject itemObj = (JsonObject) jsonObject.get("item");
364
365                    if (itemObj != null) {
366
367                        String type = itemObj.get("type").asString();
368                        String id = itemObj.get("id").asString();
369
370                        BoxItem.Info nextItemInfo;
371                        if (type.equals("folder")) {
372                            BoxFolder folder = new BoxFolder(api, id);
373                            nextItemInfo = folder.new Info(itemObj);
374                        } else if (type.equals("file")) {
375                            BoxFile file = new BoxFile(api, id);
376                            nextItemInfo = file.new Info(itemObj);
377                        } else if (type.equals("web_link")) {
378                            BoxWebLink link = new BoxWebLink(api, id);
379                            nextItemInfo = link.new Info(itemObj);
380                        } else {
381                            assert false : "Unsupported item type: " + type;
382                            throw new BoxAPIException("Unsupported item type: " + type);
383                        }
384                        return nextItemInfo;
385                    }
386                }
387                return null;
388            }
389        };
390    }
391
392    /**
393     * Gets the JsonObject representation of the Field Operation.
394     * @param fieldOperation represents the template update operation
395     * @return the json object
396     */
397    private static JsonObject getFieldOperationJsonObject(FieldOperation fieldOperation) {
398        JsonObject jsonObject = new JsonObject();
399        jsonObject.add("op", fieldOperation.getOp().toString());
400
401        String fieldKey = fieldOperation.getFieldKey();
402        if (fieldKey != null) {
403            jsonObject.add("fieldKey", fieldKey);
404        }
405
406        Field field = fieldOperation.getData();
407        if (field != null) {
408            JsonObject fieldObj = new JsonObject();
409
410            String type = field.getType();
411            if (type != null) {
412                fieldObj.add("type", type);
413            }
414
415            String key = field.getKey();
416            if (key != null) {
417                fieldObj.add("key", key);
418            }
419
420            String displayName = field.getDisplayName();
421            if (displayName != null) {
422                fieldObj.add("displayName", displayName);
423            }
424
425            String description = field.getDescription();
426            if (description != null) {
427                fieldObj.add("description", description);
428            }
429
430            Boolean hidden = field.getIsHidden();
431            if (hidden != null) {
432                fieldObj.add("hidden", hidden);
433            }
434
435            List<String> options = field.getOptions();
436            if (options != null) {
437                JsonArray array = new JsonArray();
438                for (String option: options) {
439                    JsonObject optionObj = new JsonObject();
440                    optionObj.add("key", option);
441
442                    array.add(optionObj);
443                }
444
445                fieldObj.add("options", array);
446            }
447
448            jsonObject.add("data", fieldObj);
449        }
450
451        List<String> fieldKeys = fieldOperation.getFieldKeys();
452        if (fieldKeys != null) {
453            jsonObject.add("fieldKeys", getJsonArray(fieldKeys));
454        }
455
456        List<String> enumOptionKeys = fieldOperation.getEnumOptionKeys();
457        if (enumOptionKeys != null) {
458            jsonObject.add("enumOptionKeys", getJsonArray(enumOptionKeys));
459        }
460
461        String enumOptionKey = fieldOperation.getEnumOptionKey();
462        if (enumOptionKey != null) {
463            jsonObject.add("enumOptionKey", enumOptionKey);
464        }
465
466        String multiSelectOptionKey = fieldOperation.getMultiSelectOptionKey();
467        if (multiSelectOptionKey != null) {
468            jsonObject.add("multiSelectOptionKey", multiSelectOptionKey);
469        }
470
471        List<String> multiSelectOptionKeys = fieldOperation.getMultiSelectOptionKeys();
472        if (multiSelectOptionKeys != null) {
473            jsonObject.add("multiSelectOptionKeys", getJsonArray(multiSelectOptionKeys));
474        }
475
476        return jsonObject;
477    }
478
479    /**
480     * Gets the Json Array representation of the given list of strings.
481     * @param keys List of strings
482     * @return the JsonArray represents the list of keys
483     */
484    private static JsonArray getJsonArray(List<String> keys) {
485        JsonArray array = new JsonArray();
486        for (String key : keys) {
487            array.add(key);
488        }
489
490        return array;
491    }
492
493    /**
494     * Gets the metadata template of properties.
495     * @param api the API connection to be used.
496     * @return the metadata template returned from the server.
497     */
498    public static MetadataTemplate getMetadataTemplate(BoxAPIConnection api) {
499        return getMetadataTemplate(api, DEFAULT_METADATA_TYPE);
500    }
501
502    /**
503     * Gets the metadata template of specified template type.
504     * @param api the API connection to be used.
505     * @param templateName the metadata template type name.
506     * @return the metadata template returned from the server.
507     */
508    public static MetadataTemplate getMetadataTemplate(BoxAPIConnection api, String templateName) {
509        String scope = scopeBasedOnType(templateName);
510        return getMetadataTemplate(api, templateName, scope);
511    }
512
513    /**
514     * Gets the metadata template of specified template type.
515     * @param api the API connection to be used.
516     * @param templateName the metadata template type name.
517     * @param scope the metadata template scope (global or enterprise).
518     * @param fields the fields to retrieve.
519     * @return the metadata template returned from the server.
520     */
521    public static MetadataTemplate getMetadataTemplate(
522            BoxAPIConnection api, String templateName, String scope, String ... fields) {
523        QueryStringBuilder builder = new QueryStringBuilder();
524        if (fields.length > 0) {
525            builder.appendParam("fields", fields);
526        }
527        URL url = METADATA_TEMPLATE_URL_TEMPLATE.buildWithQuery(
528                api.getBaseURL(), builder.toString(), scope, templateName);
529        BoxAPIRequest request = new BoxAPIRequest(api, url, "GET");
530        BoxJSONResponse response = (BoxJSONResponse) request.send();
531        return new MetadataTemplate(response.getJSON());
532    }
533
534    /**
535     * Geta the specified metadata template by its ID.
536     * @param api the API connection to be used.
537     * @param templateID the ID of the template to get.
538     * @return the metadata template object.
539     */
540    public static MetadataTemplate getMetadataTemplateByID(BoxAPIConnection api, String templateID) {
541
542        URL url = METADATA_TEMPLATE_BY_ID_URL_TEMPLATE.build(api.getBaseURL(), templateID);
543        BoxAPIRequest request = new BoxAPIRequest(api, url, "GET");
544        BoxJSONResponse response = (BoxJSONResponse) request.send();
545        return new MetadataTemplate(response.getJSON());
546    }
547
548    /**
549     * Returns all metadata templates within a user's enterprise.
550     * @param api the API connection to be used.
551     * @param fields the fields to retrieve.
552     * @return the metadata template returned from the server.
553     */
554    public static Iterable<MetadataTemplate> getEnterpriseMetadataTemplates(BoxAPIConnection api, String ... fields) {
555        return getEnterpriseMetadataTemplates(ENTERPRISE_METADATA_SCOPE, api, fields);
556    }
557
558    /**
559     * Returns all metadata templates within a user's scope. Currently only the enterprise scope is supported.
560     * @param scope the scope of the metadata templates.
561     * @param api the API connection to be used.
562     * @param fields the fields to retrieve.
563     * @return the metadata template returned from the server.
564     */
565    public static Iterable<MetadataTemplate> getEnterpriseMetadataTemplates(
566            String scope, BoxAPIConnection api, String ... fields) {
567        return getEnterpriseMetadataTemplates(ENTERPRISE_METADATA_SCOPE, DEFAULT_ENTRIES_LIMIT, api, fields);
568    }
569
570    /**
571     * Returns all metadata templates within a user's scope. Currently only the enterprise scope is supported.
572     * @param scope the scope of the metadata templates.
573     * @param limit maximum number of entries per response.
574     * @param api the API connection to be used.
575     * @param fields the fields to retrieve.
576     * @return the metadata template returned from the server.
577     */
578    public static Iterable<MetadataTemplate> getEnterpriseMetadataTemplates(
579            String scope, int limit, BoxAPIConnection api, String ... fields) {
580        QueryStringBuilder builder = new QueryStringBuilder();
581        if (fields.length > 0) {
582            builder.appendParam("fields", fields);
583        }
584        return new BoxResourceIterable<MetadataTemplate>(
585                api, ENTERPRISE_METADATA_URL_TEMPLATE.buildWithQuery(
586                        api.getBaseURL(), builder.toString(), scope), limit) {
587
588            @Override
589            protected MetadataTemplate factory(JsonObject jsonObject) {
590                return new MetadataTemplate(jsonObject);
591            }
592        };
593    }
594
595    /**
596     * Determines the metadata scope based on type.
597     * @param typeName type of the metadata.
598     * @return scope of the metadata.
599     */
600    private static String scopeBasedOnType(String typeName) {
601        return typeName.equals(DEFAULT_METADATA_TYPE) ? GLOBAL_METADATA_SCOPE : ENTERPRISE_METADATA_SCOPE;
602    }
603
604    /**
605     * Class contains information about the metadata template field.
606     */
607    public static class Field extends BoxJSONObject {
608
609        /**
610         * @see #getID()
611         */
612        private String id;
613
614        /**
615         * @see #getType()
616         */
617        private String type;
618
619        /**
620         * @see #getKey()
621         */
622        private String key;
623
624        /**
625         * @see #getDisplayName()
626         */
627        private String displayName;
628
629        /**
630         * @see #getIsHidden()
631         */
632        private Boolean isHidden;
633
634        /**
635         * @see #getDescription()
636         */
637        private String description;
638
639        /**
640         * @see #getOptionsObject()
641         */
642        private List<Option> options;
643
644        /**
645         * Constructs an empty metadata template.
646         */
647        public Field() {
648            super();
649        }
650
651        /**
652         * Constructs a metadate template field from a JSON string.
653         * @param json the json encoded metadate template field.
654         */
655        public Field(String json) {
656            super(json);
657        }
658
659        /**
660         * Constructs a metadate template field from a JSON object.
661         * @param jsonObject the json encoded metadate template field.
662         */
663        Field(JsonObject jsonObject) {
664            super(jsonObject);
665        }
666
667        /**
668         * Gets the ID of the template field.
669         * @return the template field ID.
670         */
671        public String getID() {
672            return this.id;
673        }
674
675        /**
676         * Gets the data type of the field's value.
677         * @return the data type of the field's value.
678         */
679        public String getType() {
680            return this.type;
681        }
682
683        /**
684         * Sets the data type of the field's value.
685         * @param type the data type of the field's value.
686         */
687        public void setType(String type) {
688            this.type = type;
689        }
690
691        /**
692         * Gets the key of the field.
693         * @return the key of the field.
694         */
695        public String getKey() {
696            return this.key;
697        }
698
699        /**
700         * Sets the key of the field.
701         * @param key the key of the field.
702         */
703        public void setKey(String key) {
704            this.key = key;
705        }
706
707        /**
708         * Gets the display name of the field.
709         * @return the display name of the field.
710         */
711        public String getDisplayName() {
712            return this.displayName;
713        }
714
715        /**
716         * Sets the display name of the field.
717         * @param displayName the display name of the field.
718         */
719        public void setDisplayName(String displayName) {
720            this.displayName = displayName;
721        }
722
723        /**
724         * Gets is metadata template field hidden.
725         * @return is metadata template field hidden.
726         */
727        public Boolean getIsHidden() {
728            return this.isHidden;
729        }
730
731        /**
732         * Sets is metadata template field hidden.
733         * @param isHidden is metadata template field hidden?
734         */
735        public void setIsHidden(boolean isHidden) {
736            this.isHidden = isHidden;
737        }
738
739        /**
740         * Gets the description of the field.
741         * @return the description of the field.
742         */
743        public String getDescription() {
744            return this.description;
745        }
746
747        /**
748         * Sets the description of the field.
749         * @param description the description of the field.
750         */
751        public void setDescription(String description) {
752            this.description = description;
753        }
754
755        /**
756         * Gets list of possible options for enum type of the field.
757         * @return list of possible options for enum type of the field.
758         */
759        public List<String> getOptions() {
760            if (this.options == null) {
761                return null;
762            }
763            List<String> optionsList = new ArrayList<String>();
764            for (Option option : this.options) {
765                optionsList.add(option.getKey());
766            }
767            return optionsList;
768        }
769
770        /**
771         * Gets list of possible options for options type of the field.
772         * @return list of possible options for option type of the field.
773         */
774        public List<Option> getOptionsObjects() {
775            return this.options;
776        }
777
778        /**
779         * Sets list of possible options for enum type of the field.
780         * @param options list of possible options for enum type of the field.
781         */
782        public void setOptions(List<String> options) {
783            if (options == null) {
784                this.options = null;
785            }
786            List<Option> optionList = new ArrayList<Option>();
787            for (String key : options) {
788                JsonObject optionObject = new JsonObject();
789                optionObject.add("key", key);
790                Option newOption = new Option(optionObject);
791                optionList.add(newOption);
792            }
793            this.options = optionList;
794        }
795
796        /**
797         * {@inheritDoc}
798         */
799        @Override
800        void parseJSONMember(JsonObject.Member member) {
801            JsonValue value = member.getValue();
802            String memberName = member.getName();
803            if (memberName.equals("type")) {
804                this.type = value.asString();
805            } else if (memberName.equals("key")) {
806                this.key = value.asString();
807            } else if (memberName.equals("displayName")) {
808                this.displayName = value.asString();
809            } else if (memberName.equals("hidden")) {
810                this.isHidden = value.asBoolean();
811            } else if (memberName.equals("description")) {
812                this.description = value.asString();
813            } else if (memberName.equals("options")) {
814                this.options = new ArrayList<Option>();
815                for (JsonValue option : value.asArray()) {
816                    this.options.add(new Option(option.asObject()));
817                }
818            } else if (memberName.equals("id")) {
819                this.id = value.asString();
820            }
821        }
822    }
823
824    /**
825     * Class contains information about the metadata template option.
826     */
827    public static class Option extends BoxJSONObject {
828        /**
829         * @see #getID()
830         */
831        private String id;
832         /**
833         * @see #getKey()
834         */
835        private String key;
836         /**
837         * Constructs an empty metadata template.
838         */
839        public Option() {
840            super();
841        }
842         /**
843         * Constructs a metadate template option from a JSON string.
844         * @param json the json encoded metadata template option.
845         */
846        public Option(String json) {
847            super(json);
848        }
849
850         /**
851         * Constructs a metadate template option from a JSON object.
852         * @param jsonObject the json encoded metadate template option.
853         */
854        Option(JsonObject jsonObject) {
855            super(jsonObject);
856        }
857         /**
858         * Gets the ID of the template field.
859         * @return the template field ID.
860         */
861        public String getID() {
862            return this.id;
863        }
864         /**
865         * Gets the key of the field.
866         * @return the key of the field.
867         */
868        public String getKey() {
869            return this.key;
870        }
871
872        /**
873         * {@inheritDoc}
874         */
875        @Override
876        void parseJSONMember(JsonObject.Member member) {
877            JsonValue value = member.getValue();
878            String memberName = member.getName();
879            if (memberName.equals("id")) {
880                this.id = value.asString();
881            } else if (memberName.equals("key")) {
882                this.key = value.asString();
883            }
884        }
885    }
886
887    /**
888     * Posssible operations that can be performed in a Metadata template.
889     *  <ul>
890     *      <li>Add an enum option</li>
891     *      <li>Edit an enum option</li>
892     *      <li>Remove an enum option</li>
893     *      <li>Add a field</li>
894     *      <li>Edit a field</li>
895     *      <li>Remove a field</li>
896     *      <li>Edit template</li>
897     *      <li>Reorder the enum option</li>
898     *      <li>Reorder the field list</li>
899     *  </ul>
900     */
901    public static class FieldOperation extends BoxJSONObject {
902
903        private Operation op;
904        private Field data;
905        private String fieldKey;
906        private List<String> fieldKeys;
907        private List<String> enumOptionKeys;
908        private String enumOptionKey;
909        private String multiSelectOptionKey;
910        private List<String> multiSelectOptionKeys;
911
912        /**
913         * Constructs an empty FieldOperation.
914         */
915        public FieldOperation() {
916            super();
917        }
918
919        /**
920         * Constructs a Field operation from a JSON string.
921         * @param json the json encoded metadate template field.
922         */
923        public FieldOperation(String json) {
924            super(json);
925        }
926
927        /**
928         * Constructs a Field operation from a JSON object.
929         * @param jsonObject the json encoded metadate template field.
930         */
931        FieldOperation(JsonObject jsonObject) {
932            super(jsonObject);
933        }
934
935        /**
936         * Gets the operation.
937         * @return the operation
938         */
939        public Operation getOp() {
940            return this.op;
941        }
942
943        /**
944         * Gets the data associated with the operation.
945         * @return the field object representing the data
946         */
947        public Field getData() {
948            return this.data;
949        }
950
951        /**
952         * Gets the field key.
953         * @return the field key
954         */
955        public String getFieldKey() {
956            return this.fieldKey;
957        }
958
959        /**
960         * Gets the list of field keys.
961         * @return the list of Strings
962         */
963        public List<String> getFieldKeys() {
964            return this.fieldKeys;
965        }
966
967        /**
968         * Gets the list of keys of the Enum options.
969         * @return the list of Strings
970         */
971        public List<String> getEnumOptionKeys() {
972            return this.enumOptionKeys;
973        }
974
975        /**
976         * Sets the operation.
977         * @param op the operation
978         */
979        public void setOp(Operation op) {
980            this.op = op;
981        }
982
983        /**
984         * Sets the data.
985         * @param data the Field object representing the data
986         */
987        public void setData(Field data) {
988            this.data = data;
989        }
990
991        /**
992         * Sets the field key.
993         * @param fieldKey the key of the field
994         */
995        public void setFieldKey(String fieldKey) {
996            this.fieldKey = fieldKey;
997        }
998
999        /**
1000         * Sets the list of the field keys.
1001         * @param fieldKeys the list of strings
1002         */
1003        public void setFieldKeys(List<String> fieldKeys) {
1004            this.fieldKeys = fieldKeys;
1005        }
1006
1007        /**
1008         * Sets the list of the enum option keys.
1009         * @param enumOptionKeys the list of Strings
1010         */
1011        public void setEnumOptionKeys(List<String> enumOptionKeys) {
1012            this.enumOptionKeys = enumOptionKeys;
1013        }
1014
1015        /**
1016         * Gets the enum option key.
1017         * @return the enum option key
1018         */
1019        public String getEnumOptionKey() {
1020            return this.enumOptionKey;
1021        }
1022
1023        /**
1024         * Sets the enum option key.
1025         * @param enumOptionKey the enum option key
1026         */
1027        public void setEnumOptionKey(String enumOptionKey) {
1028            this.enumOptionKey = enumOptionKey;
1029        }
1030
1031        /**
1032         * Gets the multi-select option key.
1033         * @return the key.
1034         */
1035        public String getMultiSelectOptionKey() {
1036            return this.multiSelectOptionKey;
1037        }
1038
1039        /**
1040         * Sets the multi-select option key.
1041         * @param key the key.
1042         */
1043        public void setMultiSelectOptionKey(String key) {
1044            this.multiSelectOptionKey = key;
1045        }
1046
1047        /**
1048         * Gets the list of multiselect option keys.
1049         * @return the list of keys.
1050         */
1051        public List<String> getMultiSelectOptionKeys() {
1052            return this.multiSelectOptionKeys;
1053        }
1054
1055        /**
1056         * Sets the multi-select option keys.
1057         * @param keys the list of keys.
1058         */
1059        public void setMultiSelectOptionKeys(List<String> keys) {
1060            this.multiSelectOptionKeys = keys;
1061        }
1062
1063        /**
1064         * {@inheritDoc}
1065         */
1066        @Override
1067        void parseJSONMember(JsonObject.Member member) {
1068            JsonValue value = member.getValue();
1069            String memberName = member.getName();
1070            if (memberName.equals("op")) {
1071                this.op = Operation.valueOf(value.asString());
1072            } else if (memberName.equals("data")) {
1073                this.data = new Field(value.asObject());
1074            } else if (memberName.equals("fieldKey")) {
1075                this.fieldKey = value.asString();
1076            } else if (memberName.equals("fieldKeys")) {
1077                if (this.fieldKeys == null) {
1078                    this.fieldKeys = new ArrayList<String>();
1079                } else {
1080                    this.fieldKeys.clear();
1081                }
1082
1083                JsonArray array = value.asArray();
1084                for (JsonValue jsonValue: array) {
1085                    this.fieldKeys.add(jsonValue.asString());
1086                }
1087            } else if (memberName.equals("enumOptionKeys")) {
1088                if (this.enumOptionKeys == null) {
1089                    this.enumOptionKeys = new ArrayList<String>();
1090                } else {
1091                    this.enumOptionKeys.clear();
1092                }
1093
1094                JsonArray array = value.asArray();
1095                for (JsonValue jsonValue: array) {
1096                    this.enumOptionKeys.add(jsonValue.asString());
1097                }
1098            } else if (memberName.equals("enumOptionKey")) {
1099                this.enumOptionKey = value.asString();
1100            } else if (memberName.equals("multiSelectOptionKey")) {
1101                this.multiSelectOptionKey = value.asString();
1102            } else if (memberName.equals("multiSelectOptionKeys")) {
1103                this.multiSelectOptionKeys = new ArrayList<String>();
1104                for (JsonValue key : value.asArray()) {
1105                    this.multiSelectOptionKeys.add(key.asString());
1106                }
1107            }
1108        }
1109    }
1110
1111    /**
1112     * Possible template operations.
1113     */
1114    public enum Operation {
1115
1116        /**
1117         * Adds an enum option at the end of the enum option list for the specified field.
1118         */
1119        addEnumOption,
1120
1121        /**
1122         * Edits the enum option.
1123         */
1124        editEnumOption,
1125
1126        /**
1127         * Removes the specified enum option from the specified enum field.
1128         */
1129        removeEnumOption,
1130
1131        /**
1132         * Adds a field at the end of the field list for the template.
1133         */
1134        addField,
1135
1136        /**
1137         * Edits any number of the base properties of a field: displayName, hidden, description.
1138         */
1139        editField,
1140
1141        /**
1142         * Removes the specified field from the template.
1143         */
1144        removeField,
1145
1146        /**
1147         * Edits any number of the base properties of a template: displayName, hidden.
1148         */
1149        editTemplate,
1150
1151        /**
1152         * Reorders the enum option list to match the requested enum option list.
1153         */
1154        reorderEnumOptions,
1155
1156        /**
1157         * Reorders the field list to match the requested field list.
1158         */
1159        reorderFields,
1160
1161        /**
1162         * Adds a new option to a multiselect field.
1163         */
1164        addMultiSelectOption,
1165
1166        /**
1167         * Edits an existing option in a multiselect field.
1168         */
1169        editMultiSelectOption,
1170
1171        /**
1172         * Removes an option from a multiselect field.
1173         */
1174        removeMultiSelectOption,
1175
1176        /**
1177         * Changes the display order of options in a multiselect field.
1178         */
1179        reorderMultiSelectOptions
1180    }
1181}