001package com.box.sdk;
002
003import com.eclipsesource.json.Json;
004import com.eclipsesource.json.JsonArray;
005import com.eclipsesource.json.JsonObject;
006import com.eclipsesource.json.JsonValue;
007import java.net.URL;
008import java.util.ArrayList;
009import java.util.List;
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://developer.box.com/reference/resources/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     * @see #getCopyInstanceOnItemCopy()
099     */
100    private Boolean copyInstanceOnItemCopy;
101
102    /**
103     * Constructs an empty metadata template.
104     */
105    public MetadataTemplate() {
106        super();
107    }
108
109    /**
110     * Constructs a metadata template from a JSON string.
111     *
112     * @param json the json encoded metadate template.
113     */
114    public MetadataTemplate(String json) {
115        super(json);
116    }
117
118    /**
119     * Constructs a metadate template from a JSON object.
120     *
121     * @param jsonObject the json encoded metadate template.
122     */
123    MetadataTemplate(JsonObject jsonObject) {
124        super(jsonObject);
125    }
126
127    /**
128     * Creates new metadata template.
129     *
130     * @param api         the API connection to be used.
131     * @param scope       the scope of the object.
132     * @param templateKey a unique identifier for the template.
133     * @param displayName the display name of the field.
134     * @param hidden      whether this template is hidden in the UI.
135     * @param fields      the ordered set of fields for the template
136     * @return the metadata template returned from the server.
137     */
138    public static MetadataTemplate createMetadataTemplate(
139        BoxAPIConnection api,
140        String scope,
141        String templateKey,
142        String displayName,
143        boolean hidden,
144        List<Field> fields
145    ) {
146        return createMetadataTemplate(api, scope, templateKey, displayName, hidden, fields, null);
147    }
148
149    /**
150     * Creates new metadata template.
151     *
152     * @param api                    the API connection to be used.
153     * @param scope                  the scope of the object.
154     * @param templateKey            a unique identifier for the template.
155     * @param displayName            the display name of the field.
156     * @param hidden                 whether this template is hidden in the UI.
157     * @param fields                 the ordered set of fields for the template
158     * @param copyInstanceOnItemCopy determines whether the copy operation should copy the metadata along with the item.
159     * @return the metadata template returned from the server.
160     */
161    public static MetadataTemplate createMetadataTemplate(
162        BoxAPIConnection api,
163        String scope,
164        String templateKey,
165        String displayName,
166        Boolean hidden,
167        List<Field> fields,
168        Boolean copyInstanceOnItemCopy
169    ) {
170
171        JsonObject jsonObject = new JsonObject();
172        jsonObject.add("scope", scope);
173        jsonObject.add("displayName", displayName);
174
175        if (hidden != null) {
176            jsonObject.add("hidden", hidden);
177        }
178
179        if (copyInstanceOnItemCopy != null) {
180            jsonObject.add("copyInstanceOnItemCopy", copyInstanceOnItemCopy);
181        }
182
183        if (templateKey != null) {
184            jsonObject.add("templateKey", templateKey);
185        }
186
187        JsonArray fieldsArray = new JsonArray();
188        if (fields != null && !fields.isEmpty()) {
189            for (Field field : fields) {
190                JsonObject fieldObj = getFieldJsonObject(field);
191
192                fieldsArray.add(fieldObj);
193            }
194
195            jsonObject.add("fields", fieldsArray);
196        }
197
198        URL url = METADATA_TEMPLATE_SCHEMA_URL_TEMPLATE.build(api.getBaseURL());
199        BoxJSONRequest request = new BoxJSONRequest(api, url, "POST");
200        request.setBody(jsonObject.toString());
201
202        try (BoxJSONResponse response = request.send()) {
203            JsonObject responseJSON = Json.parse(response.getJSON()).asObject();
204
205            return new MetadataTemplate(responseJSON);
206        }
207    }
208
209    /**
210     * Gets the JsonObject representation of the given field object.
211     *
212     * @param field represents a template field
213     * @return the json object
214     */
215    private static JsonObject getFieldJsonObject(Field field) {
216        JsonObject fieldObj = new JsonObject();
217        fieldObj.add("type", field.getType());
218        fieldObj.add("key", field.getKey());
219        fieldObj.add("displayName", field.getDisplayName());
220
221        String fieldDesc = field.getDescription();
222        if (fieldDesc != null) {
223            fieldObj.add("description", field.getDescription());
224        }
225
226        Boolean fieldIsHidden = field.getIsHidden();
227        if (fieldIsHidden != null) {
228            fieldObj.add("hidden", field.getIsHidden());
229        }
230
231        JsonArray array = new JsonArray();
232        List<String> options = field.getOptions();
233        if (options != null && !options.isEmpty()) {
234            for (String option : options) {
235                JsonObject optionObj = new JsonObject();
236                optionObj.add("key", option);
237
238                array.add(optionObj);
239            }
240            fieldObj.add("options", array);
241        }
242
243        return fieldObj;
244    }
245
246    /**
247     * Updates the schema of an existing metadata template.
248     *
249     * @param api             the API connection to be used
250     * @param scope           the scope of the object
251     * @param template        Unique identifier of the template
252     * @param fieldOperations the fields that needs to be updated / added in the template
253     * @return the updated metadata template
254     */
255    public static MetadataTemplate updateMetadataTemplate(BoxAPIConnection api, String scope, String template,
256                                                          List<FieldOperation> fieldOperations) {
257
258        JsonArray array = new JsonArray();
259
260        for (FieldOperation fieldOperation : fieldOperations) {
261            JsonObject jsonObject = getFieldOperationJsonObject(fieldOperation);
262            array.add(jsonObject);
263        }
264
265        URL url = METADATA_TEMPLATE_URL_TEMPLATE.buildAlpha(api.getBaseURL(), scope, template);
266        BoxJSONRequest request = new BoxJSONRequest(api, url, "PUT");
267        request.setBody(array.toString());
268
269        try (BoxJSONResponse response = request.send()) {
270            JsonObject responseJson = Json.parse(response.getJSON()).asObject();
271
272            return new MetadataTemplate(responseJson);
273        }
274    }
275
276    /**
277     * Deletes the schema of an existing metadata template.
278     *
279     * @param api      the API connection to be used
280     * @param scope    the scope of the object
281     * @param template Unique identifier of the template
282     */
283    public static void deleteMetadataTemplate(BoxAPIConnection api, String scope, String template) {
284        URL url = METADATA_TEMPLATE_URL_TEMPLATE.buildAlpha(api.getBaseURL(), scope, template);
285        BoxAPIRequest request = new BoxAPIRequest(api, url, "DELETE");
286        request.send().close();
287    }
288
289    /**
290     * Executes a metadata query.
291     *
292     * @param api       The API connection to be used
293     * @param queryBody The query
294     * @return An iterable of BoxItem.Info search results
295     */
296    public static BoxResourceIterable<BoxItem.Info> executeMetadataQuery(
297        final BoxAPIConnection api,
298        final MetadataQuery queryBody
299    ) {
300
301        URL url = METADATA_QUERIES_URL_TEMPLATE.build(api.getBaseURL());
302        return new BoxResourceIterable<BoxItem.Info>(
303            api, url, queryBody.getLimit(), queryBody.toJsonObject(), queryBody.getMarker()
304        ) {
305
306            @Override
307            protected BoxItem.Info factory(JsonObject jsonObject) {
308                String type = jsonObject.get("type").asString();
309                String id = jsonObject.get("id").asString();
310
311                BoxItem.Info nextItemInfo;
312                switch (type) {
313                    case "folder":
314                        BoxFolder folder = new BoxFolder(api, id);
315                        nextItemInfo = folder.new Info(jsonObject);
316                        break;
317                    case "file":
318                        BoxFile file = new BoxFile(api, id);
319                        nextItemInfo = file.new Info(jsonObject);
320                        break;
321                    case "web_link":
322                        BoxWebLink link = new BoxWebLink(api, id);
323                        nextItemInfo = link.new Info(jsonObject);
324                        break;
325                    default:
326                        assert false : "Unsupported item type: " + type;
327                        throw new BoxAPIException("Unsupported item type: " + type);
328                }
329
330                return nextItemInfo;
331            }
332        };
333    }
334
335    /**
336     * Gets the JsonObject representation of the Field Operation.
337     *
338     * @param fieldOperation represents the template update operation
339     * @return the json object
340     */
341    private static JsonObject getFieldOperationJsonObject(FieldOperation fieldOperation) {
342        JsonObject jsonObject = new JsonObject();
343        jsonObject.add("op", fieldOperation.getOp().toString());
344
345        String fieldKey = fieldOperation.getFieldKey();
346        if (fieldKey != null) {
347            jsonObject.add("fieldKey", fieldKey);
348        }
349
350        Field field = fieldOperation.getData();
351        if (field != null) {
352            JsonObject fieldObj = new JsonObject();
353
354            String type = field.getType();
355            if (type != null) {
356                fieldObj.add("type", type);
357            }
358
359            String key = field.getKey();
360            if (key != null) {
361                fieldObj.add("key", key);
362            }
363
364            String displayName = field.getDisplayName();
365            if (displayName != null) {
366                fieldObj.add("displayName", displayName);
367            }
368
369            String description = field.getDescription();
370            if (description != null) {
371                fieldObj.add("description", description);
372            }
373
374            Boolean hidden = field.getIsHidden();
375            if (hidden != null) {
376                fieldObj.add("hidden", hidden);
377            }
378
379            List<String> options = field.getOptions();
380            if (options != null) {
381                JsonArray array = new JsonArray();
382                for (String option : options) {
383                    JsonObject optionObj = new JsonObject();
384                    optionObj.add("key", option);
385
386                    array.add(optionObj);
387                }
388
389                fieldObj.add("options", array);
390            }
391
392            Boolean copyInstanceOnItemCopy = field.getCopyInstanceOnItemCopy();
393            if (copyInstanceOnItemCopy != null) {
394                fieldObj.add("copyInstanceOnItemCopy", copyInstanceOnItemCopy);
395            }
396
397            StaticConfig staticConfig = field.getStaticConfig();
398            if (staticConfig != null) {
399                JsonObject staticConfigObj = new JsonObject();
400                JsonObject classification = staticConfig.getClassification();
401                if (classification != null) {
402                    staticConfigObj.add("classification", classification);
403                }
404                fieldObj.add("staticConfig", staticConfigObj);
405            }
406
407            jsonObject.add("data", fieldObj);
408        }
409
410        List<String> fieldKeys = fieldOperation.getFieldKeys();
411        if (fieldKeys != null) {
412            jsonObject.add("fieldKeys", getJsonArray(fieldKeys));
413        }
414
415        List<String> enumOptionKeys = fieldOperation.getEnumOptionKeys();
416        if (enumOptionKeys != null) {
417            jsonObject.add("enumOptionKeys", getJsonArray(enumOptionKeys));
418        }
419
420        String enumOptionKey = fieldOperation.getEnumOptionKey();
421        if (enumOptionKey != null) {
422            jsonObject.add("enumOptionKey", enumOptionKey);
423        }
424
425        String multiSelectOptionKey = fieldOperation.getMultiSelectOptionKey();
426        if (multiSelectOptionKey != null) {
427            jsonObject.add("multiSelectOptionKey", multiSelectOptionKey);
428        }
429
430        List<String> multiSelectOptionKeys = fieldOperation.getMultiSelectOptionKeys();
431        if (multiSelectOptionKeys != null) {
432            jsonObject.add("multiSelectOptionKeys", getJsonArray(multiSelectOptionKeys));
433        }
434
435        return jsonObject;
436    }
437
438    /**
439     * Gets the Json Array representation of the given list of strings.
440     *
441     * @param keys List of strings
442     * @return the JsonArray represents the list of keys
443     */
444    private static JsonArray getJsonArray(List<String> keys) {
445        JsonArray array = new JsonArray();
446        for (String key : keys) {
447            array.add(key);
448        }
449
450        return array;
451    }
452
453    /**
454     * Gets the metadata template of properties.
455     *
456     * @param api the API connection to be used.
457     * @return the metadata template returned from the server.
458     */
459    public static MetadataTemplate getMetadataTemplate(BoxAPIConnection api) {
460        return getMetadataTemplate(api, DEFAULT_METADATA_TYPE);
461    }
462
463    /**
464     * Gets the metadata template of specified template type.
465     *
466     * @param api          the API connection to be used.
467     * @param templateName the metadata template type name.
468     * @return the metadata template returned from the server.
469     */
470    public static MetadataTemplate getMetadataTemplate(BoxAPIConnection api, String templateName) {
471        String scope = scopeBasedOnType(templateName);
472        return getMetadataTemplate(api, templateName, scope);
473    }
474
475    /**
476     * Gets the metadata template of specified template type.
477     *
478     * @param api          the API connection to be used.
479     * @param templateName the metadata template type name.
480     * @param scope        the metadata template scope (global or enterprise).
481     * @param fields       the fields to retrieve.
482     * @return the metadata template returned from the server.
483     */
484    public static MetadataTemplate getMetadataTemplate(
485        BoxAPIConnection api, String templateName, String scope, String... fields) {
486        QueryStringBuilder builder = new QueryStringBuilder();
487        if (fields.length > 0) {
488            builder.appendParam("fields", fields);
489        }
490        URL url = METADATA_TEMPLATE_URL_TEMPLATE.buildAlphaWithQuery(
491            api.getBaseURL(), builder.toString(), scope, templateName);
492        BoxJSONRequest request = new BoxJSONRequest(api, url, "GET");
493        try (BoxJSONResponse response = request.send()) {
494            return new MetadataTemplate(response.getJSON());
495        }
496    }
497
498    /**
499     * Geta the specified metadata template by its ID.
500     *
501     * @param api        the API connection to be used.
502     * @param templateID the ID of the template to get.
503     * @return the metadata template object.
504     */
505    public static MetadataTemplate getMetadataTemplateByID(BoxAPIConnection api, String templateID) {
506
507        URL url = METADATA_TEMPLATE_BY_ID_URL_TEMPLATE.buildAlpha(api.getBaseURL(), templateID);
508        BoxJSONRequest request = new BoxJSONRequest(api, url, "GET");
509        try (BoxJSONResponse response = request.send()) {
510            return new MetadataTemplate(response.getJSON());
511        }
512    }
513
514    /**
515     * Returns all metadata templates within a user's enterprise.
516     *
517     * @param api    the API connection to be used.
518     * @param fields the fields to retrieve.
519     * @return the metadata template returned from the server.
520     */
521    public static Iterable<MetadataTemplate> getEnterpriseMetadataTemplates(BoxAPIConnection api, String... fields) {
522        return getEnterpriseMetadataTemplates(ENTERPRISE_METADATA_SCOPE, api, fields);
523    }
524
525    /**
526     * Returns all metadata templates within a user's scope. Currently only the enterprise scope is supported.
527     *
528     * @param scope  the scope of the metadata templates.
529     * @param api    the API connection to be used.
530     * @param fields the fields to retrieve.
531     * @return the metadata template returned from the server.
532     */
533    public static Iterable<MetadataTemplate> getEnterpriseMetadataTemplates(
534        String scope, BoxAPIConnection api, String... fields
535    ) {
536        return getEnterpriseMetadataTemplates(ENTERPRISE_METADATA_SCOPE, DEFAULT_ENTRIES_LIMIT, api, fields);
537    }
538
539    /**
540     * Returns all metadata templates within a user's scope. Currently only the enterprise scope is supported.
541     *
542     * @param scope  the scope of the metadata templates.
543     * @param limit  maximum number of entries per response.
544     * @param api    the API connection to be used.
545     * @param fields the fields to retrieve.
546     * @return the metadata template returned from the server.
547     */
548    public static Iterable<MetadataTemplate> getEnterpriseMetadataTemplates(
549        String scope, int limit, BoxAPIConnection api, String... fields) {
550        QueryStringBuilder builder = new QueryStringBuilder();
551        if (fields.length > 0) {
552            builder.appendParam("fields", fields);
553        }
554        return new BoxResourceIterable<MetadataTemplate>(
555            api, ENTERPRISE_METADATA_URL_TEMPLATE.buildAlphaWithQuery(
556            api.getBaseURL(), builder.toString(), scope), limit) {
557
558            @Override
559            protected MetadataTemplate factory(JsonObject jsonObject) {
560                return new MetadataTemplate(jsonObject);
561            }
562        };
563    }
564
565    /**
566     * Determines the metadata scope based on type.
567     *
568     * @param typeName type of the metadata.
569     * @return scope of the metadata.
570     */
571    private static String scopeBasedOnType(String typeName) {
572        return typeName.equals(DEFAULT_METADATA_TYPE) ? GLOBAL_METADATA_SCOPE : ENTERPRISE_METADATA_SCOPE;
573    }
574
575    /**
576     * Gets the ID of the template.
577     *
578     * @return the template ID.
579     */
580    public String getID() {
581        return this.id;
582    }
583
584    /**
585     * Gets the unique template key to identify the metadata template.
586     *
587     * @return the unique template key to identify the metadata template.
588     */
589    public String getTemplateKey() {
590        return this.templateKey;
591    }
592
593    /**
594     * Gets the metadata template scope.
595     *
596     * @return the metadata template scope.
597     */
598    public String getScope() {
599        return this.scope;
600    }
601
602    /**
603     * Gets the displayed metadata template name.
604     *
605     * @return the displayed metadata template name.
606     */
607    public String getDisplayName() {
608        return this.displayName;
609    }
610
611    /**
612     * Gets is the metadata template hidden.
613     *
614     * @return is the metadata template hidden.
615     */
616    public Boolean getIsHidden() {
617        return this.isHidden;
618    }
619
620    /**
621     * Gets the iterable with all fields the metadata template contains.
622     *
623     * @return the iterable with all fields the metadata template contains.
624     */
625    public List<Field> getFields() {
626        return this.fields;
627    }
628
629    /**
630     * Gets whether the copy operation should copy the metadata along with the item.
631     *
632     * @return whether the copy operation should copy the metadata along with the item.
633     */
634    public Boolean getCopyInstanceOnItemCopy() {
635        return this.copyInstanceOnItemCopy;
636    }
637
638    /**
639     * {@inheritDoc}
640     */
641    @Override
642    void parseJSONMember(JsonObject.Member member) {
643        JsonValue value = member.getValue();
644        String memberName = member.getName();
645        switch (memberName) {
646            case "templateKey":
647                this.templateKey = value.asString();
648                break;
649            case "scope":
650                this.scope = value.asString();
651                break;
652            case "displayName":
653                this.displayName = value.asString();
654                break;
655            case "hidden":
656                this.isHidden = value.asBoolean();
657                break;
658            case "fields":
659                this.fields = new ArrayList<>();
660                for (JsonValue field : value.asArray()) {
661                    this.fields.add(new Field(field.asObject()));
662                }
663                break;
664            case "id":
665                this.id = value.asString();
666                break;
667            case "copyInstanceOnItemCopy":
668                this.copyInstanceOnItemCopy = value.asBoolean();
669                break;
670            default:
671                break;
672        }
673    }
674
675    /**
676     * Possible template operations.
677     */
678    public enum Operation {
679
680        /**
681         * Adds an enum option at the end of the enum option list for the specified field.
682         */
683        addEnumOption,
684
685        /**
686         * Edits the enum option.
687         */
688        editEnumOption,
689
690        /**
691         * Removes the specified enum option from the specified enum field.
692         */
693        removeEnumOption,
694
695        /**
696         * Adds a field at the end of the field list for the template.
697         */
698        addField,
699
700        /**
701         * Edits any number of the base properties of a field: displayName, hidden, description.
702         */
703        editField,
704
705        /**
706         * Removes the specified field from the template.
707         */
708        removeField,
709
710        /**
711         * Edits any number of the base properties of a template: displayName, hidden.
712         */
713        editTemplate,
714
715        /**
716         * Reorders the enum option list to match the requested enum option list.
717         */
718        reorderEnumOptions,
719
720        /**
721         * Reorders the field list to match the requested field list.
722         */
723        reorderFields,
724
725        /**
726         * Adds a new option to a multiselect field.
727         */
728        addMultiSelectOption,
729
730        /**
731         * Edits an existing option in a multiselect field.
732         */
733        editMultiSelectOption,
734
735        /**
736         * Removes an option from a multiselect field.
737         */
738        removeMultiSelectOption,
739
740        /**
741         * Changes the display order of options in a multiselect field.
742         */
743        reorderMultiSelectOptions
744    }
745
746    /**
747     * Class contains information about the static configuration for the classification.
748     */
749    public static class StaticConfig extends BoxJSONObject {
750        private JsonObject classification;
751
752        /**
753         * Constructs an empty static configuration.
754         */
755        public StaticConfig() {
756            super();
757        }
758
759        /**
760         * Constructs a static configuration from a JSON string.
761         *
762         * @param json the json encoded metadate template field.
763         */
764        public StaticConfig(String json) {
765            super(json);
766        }
767
768        /** Constructs a static configuration from a JSON object.
769         *
770         * @param jsonObject the json encoded metadate template field.
771         */
772        StaticConfig(JsonObject jsonObject) {
773            super(jsonObject);
774        }
775
776        /**
777         * Gets the classification of the static configuration.
778         *
779         * @return the classification of the static configuration.
780         */
781        public JsonObject getClassification() {
782            return this.classification;
783        }
784
785        /**
786         * Sets the classification of the static configuration.
787         *
788         * @param classification the classification of the static configuration.
789         */
790        public void setClassification(JsonObject classification) {
791            this.classification = classification;
792        }
793
794        /**
795         * {@inheritDoc}
796         */
797        @Override
798        void parseJSONMember(JsonObject.Member member) {
799            JsonValue value = member.getValue();
800            String memberName = member.getName();
801            switch (memberName) {
802                case "classification":
803                    this.classification = value.asObject();
804                    break;
805                default:
806                    break;
807            }
808        }
809    }
810
811    /**
812     * Class contains information about the metadata template field.
813     */
814    public static class Field extends BoxJSONObject {
815
816        /**
817         * @see #getID()
818         */
819        private String id;
820
821        /**
822         * @see #getType()
823         */
824        private String type;
825
826        /**
827         * @see #getKey()
828         */
829        private String key;
830
831        /**
832         * @see #getDisplayName()
833         */
834        private String displayName;
835
836        /**
837         * @see #getIsHidden()
838         */
839        private Boolean isHidden;
840
841        /**
842         * @see #getDescription()
843         */
844        private String description;
845
846        /**
847         * @see #getOptionsObjects()
848         */
849        private List<Option> options;
850
851        /**
852         * @see #getCopyInstanceOnItemCopy()
853         */
854        private Boolean copyInstanceOnItemCopy;
855
856        /**
857         * @see #getStaticConfig()
858         */
859        private StaticConfig staticConfig;
860
861        /**
862         * Constructs an empty metadata template.
863         */
864        public Field() {
865            super();
866        }
867
868        /**
869         * Constructs a metadate template field from a JSON string.
870         *
871         * @param json the json encoded metadate template field.
872         */
873        public Field(String json) {
874            super(json);
875        }
876
877        /**
878         * Constructs a metadate template field from a JSON object.
879         *
880         * @param jsonObject the json encoded metadate template field.
881         */
882        Field(JsonObject jsonObject) {
883            super(jsonObject);
884        }
885
886        /**
887         * Gets the ID of the template field.
888         *
889         * @return the template field ID.
890         */
891        public String getID() {
892            return this.id;
893        }
894
895        /**
896         * Gets the data type of the field's value.
897         *
898         * @return the data type of the field's value.
899         */
900        public String getType() {
901            return this.type;
902        }
903
904        /**
905         * Sets the data type of the field's value.
906         *
907         * @param type the data type of the field's value.
908         */
909        public void setType(String type) {
910            this.type = type;
911        }
912
913        /**
914         * Gets the key of the field.
915         *
916         * @return the key of the field.
917         */
918        public String getKey() {
919            return this.key;
920        }
921
922        /**
923         * Sets the key of the field.
924         *
925         * @param key the key of the field.
926         */
927        public void setKey(String key) {
928            this.key = key;
929        }
930
931        /**
932         * Gets the display name of the field.
933         *
934         * @return the display name of the field.
935         */
936        public String getDisplayName() {
937            return this.displayName;
938        }
939
940        /**
941         * Sets the display name of the field.
942         *
943         * @param displayName the display name of the field.
944         */
945        public void setDisplayName(String displayName) {
946            this.displayName = displayName;
947        }
948
949        /**
950         * Gets is metadata template field hidden.
951         *
952         * @return is metadata template field hidden.
953         */
954        public Boolean getIsHidden() {
955            return this.isHidden;
956        }
957
958        /**
959         * Sets is metadata template field hidden.
960         *
961         * @param isHidden is metadata template field hidden?
962         */
963        public void setIsHidden(boolean isHidden) {
964            this.isHidden = isHidden;
965        }
966
967        /**
968         * Gets the description of the field.
969         *
970         * @return the description of the field.
971         */
972        public String getDescription() {
973            return this.description;
974        }
975
976        /**
977         * Sets the description of the field.
978         *
979         * @param description the description of the field.
980         */
981        public void setDescription(String description) {
982            this.description = description;
983        }
984
985        /**
986         * Gets list of possible options for enum type of the field.
987         *
988         * @return list of possible options for enum type of the field.
989         */
990        public List<String> getOptions() {
991            if (this.options == null) {
992                return null;
993            }
994            List<String> optionsList = new ArrayList<>();
995            for (Option option : this.options) {
996                optionsList.add(option.getKey());
997            }
998            return optionsList;
999        }
1000
1001        /**
1002         * Sets list of possible options for enum type of the field.
1003         *
1004         * @param options list of possible options for enum type of the field.
1005         */
1006        public void setOptions(List<String> options) {
1007            if (options == null) {
1008                this.options = null;
1009                return;
1010            }
1011            List<Option> optionList = new ArrayList<>();
1012            for (String key : options) {
1013                JsonObject optionObject = new JsonObject();
1014                optionObject.add("key", key);
1015                Option newOption = new Option(optionObject);
1016                optionList.add(newOption);
1017            }
1018            this.options = optionList;
1019        }
1020
1021        /**
1022         * Gets list of possible options for options type of the field.
1023         *
1024         * @return list of possible options for option type of the field.
1025         */
1026        public List<Option> getOptionsObjects() {
1027            return this.options;
1028        }
1029
1030        /**
1031         * Gets whether the copy operation should copy the metadata along with the item.
1032         *
1033         * @return whether the copy operation should copy the metadata along with the item.
1034         */
1035        public Boolean getCopyInstanceOnItemCopy() {
1036            return this.copyInstanceOnItemCopy;
1037        }
1038
1039        /**
1040         * Sets whether the copy operation should copy the metadata along with the item.
1041         *
1042         * @param copyInstanceOnItemCopy whether the copy operation should copy the metadata along with the item.
1043         */
1044        public void setCopyInstanceOnItemCopy(Boolean copyInstanceOnItemCopy) {
1045            this.copyInstanceOnItemCopy = copyInstanceOnItemCopy;
1046        }
1047
1048        /**
1049         * Gets static configuration for the classification.
1050         *
1051         * @return static configuration for the classification.
1052         */
1053        public StaticConfig getStaticConfig() {
1054            return this.staticConfig;
1055        }
1056
1057        /**
1058         * Sets static configuration for the classification.
1059         *
1060         * @param staticConfig static configuration for the classification.
1061         */
1062        public void setStaticConfig(StaticConfig staticConfig) {
1063            this.staticConfig = staticConfig;
1064        }
1065
1066        /**
1067         * {@inheritDoc}
1068         */
1069        @Override
1070        void parseJSONMember(JsonObject.Member member) {
1071            JsonValue value = member.getValue();
1072            String memberName = member.getName();
1073            switch (memberName) {
1074                case "type":
1075                    this.type = value.asString();
1076                    break;
1077                case "key":
1078                    this.key = value.asString();
1079                    break;
1080                case "displayName":
1081                    this.displayName = value.asString();
1082                    break;
1083                case "hidden":
1084                    this.isHidden = value.asBoolean();
1085                    break;
1086                case "description":
1087                    this.description = value.asString();
1088                    break;
1089                case "options":
1090                    this.options = new ArrayList<>();
1091                    for (JsonValue option : value.asArray()) {
1092                        this.options.add(new Option(option.asObject()));
1093                    }
1094                    break;
1095                case "id":
1096                    this.id = value.asString();
1097                    break;
1098                case "copyInstanceOnItemCopy":
1099                    this.copyInstanceOnItemCopy = value.asBoolean();
1100                    break;
1101                case "staticConfig":
1102                    this.staticConfig = new StaticConfig(value.asObject());
1103                    break;
1104                default:
1105                    break;
1106            }
1107        }
1108    }
1109
1110    /**
1111     * Class contains information about the metadata template option.
1112     */
1113    public static class Option extends BoxJSONObject {
1114        /**
1115         * @see #getID()
1116         */
1117        private String id;
1118        /**
1119         * @see #getKey()
1120         */
1121        private String key;
1122        /**
1123         * @see #getStaticConfig()
1124         */
1125        private StaticConfig staticConfig;
1126
1127        /**
1128         * Constructs an empty metadata template.
1129         */
1130        public Option() {
1131            super();
1132        }
1133
1134        /**
1135         * Constructs a metadate template option from a JSON string.
1136         *
1137         * @param json the json encoded metadata template option.
1138         */
1139        public Option(String json) {
1140            super(json);
1141        }
1142
1143        /**
1144         * Constructs a metadate template option from a JSON object.
1145         *
1146         * @param jsonObject the json encoded metadate template option.
1147         */
1148        Option(JsonObject jsonObject) {
1149            super(jsonObject);
1150        }
1151
1152        /**
1153         * Gets the ID of the template field.
1154         *
1155         * @return the template field ID.
1156         */
1157        public String getID() {
1158            return this.id;
1159        }
1160
1161        /**
1162         * Gets the key of the field.
1163         *
1164         * @return the key of the field.
1165         */
1166        public String getKey() {
1167            return this.key;
1168        }
1169
1170        /**
1171         * Gets static configuration for the classification.
1172         *
1173         * @return static configuration for the classification.
1174         */
1175        public StaticConfig getStaticConfig() {
1176            return this.staticConfig;
1177        }
1178
1179        /**
1180         * {@inheritDoc}
1181         */
1182        @Override
1183        void parseJSONMember(JsonObject.Member member) {
1184            JsonValue value = member.getValue();
1185            String memberName = member.getName();
1186            switch (memberName) {
1187                case "id":
1188                    this.id = value.asString();
1189                    break;
1190                case "key":
1191                    this.key = value.asString();
1192                    break;
1193                case "staticConfig":
1194                    this.staticConfig = new StaticConfig(value.asObject());
1195                    break;
1196                default:
1197                    break;
1198            }
1199        }
1200    }
1201
1202    /**
1203     * Posssible operations that can be performed in a Metadata template.
1204     * <ul>
1205     *     <li>Add an enum option</li>
1206     *     <li>Edit an enum option</li>
1207     *     <li>Remove an enum option</li>
1208     *     <li>Add a field</li>
1209     *     <li>Edit a field</li>
1210     *     <li>Remove a field</li>
1211     *     <li>Edit template</li>
1212     *     <li>Reorder the enum option</li>
1213     *     <li>Reorder the field list</li>
1214     * </ul>
1215     */
1216    public static class FieldOperation extends BoxJSONObject {
1217
1218        private Operation op;
1219        private Field data;
1220        private String fieldKey;
1221        private List<String> fieldKeys;
1222        private List<String> enumOptionKeys;
1223        private String enumOptionKey;
1224        private String multiSelectOptionKey;
1225        private List<String> multiSelectOptionKeys;
1226
1227        /**
1228         * Constructs an empty FieldOperation.
1229         */
1230        public FieldOperation() {
1231            super();
1232        }
1233
1234        /**
1235         * Constructs a Field operation from a JSON string.
1236         *
1237         * @param json the json encoded metadate template field.
1238         */
1239        public FieldOperation(String json) {
1240            super(json);
1241        }
1242
1243        /**
1244         * Constructs a Field operation from a JSON object.
1245         *
1246         * @param jsonObject the json encoded metadate template field.
1247         */
1248        FieldOperation(JsonObject jsonObject) {
1249            super(jsonObject);
1250        }
1251
1252        /**
1253         * Gets the operation.
1254         *
1255         * @return the operation
1256         */
1257        public Operation getOp() {
1258            return this.op;
1259        }
1260
1261        /**
1262         * Sets the operation.
1263         *
1264         * @param op the operation
1265         */
1266        public void setOp(Operation op) {
1267            this.op = op;
1268        }
1269
1270        /**
1271         * Gets the data associated with the operation.
1272         *
1273         * @return the field object representing the data
1274         */
1275        public Field getData() {
1276            return this.data;
1277        }
1278
1279        /**
1280         * Sets the data.
1281         *
1282         * @param data the Field object representing the data
1283         */
1284        public void setData(Field data) {
1285            this.data = data;
1286        }
1287
1288        /**
1289         * Gets the field key.
1290         *
1291         * @return the field key
1292         */
1293        public String getFieldKey() {
1294            return this.fieldKey;
1295        }
1296
1297        /**
1298         * Sets the field key.
1299         *
1300         * @param fieldKey the key of the field
1301         */
1302        public void setFieldKey(String fieldKey) {
1303            this.fieldKey = fieldKey;
1304        }
1305
1306        /**
1307         * Gets the list of field keys.
1308         *
1309         * @return the list of Strings
1310         */
1311        public List<String> getFieldKeys() {
1312            return this.fieldKeys;
1313        }
1314
1315        /**
1316         * Sets the list of the field keys.
1317         *
1318         * @param fieldKeys the list of strings
1319         */
1320        public void setFieldKeys(List<String> fieldKeys) {
1321            this.fieldKeys = fieldKeys;
1322        }
1323
1324        /**
1325         * Gets the list of keys of the Enum options.
1326         *
1327         * @return the list of Strings
1328         */
1329        public List<String> getEnumOptionKeys() {
1330            return this.enumOptionKeys;
1331        }
1332
1333        /**
1334         * Sets the list of the enum option keys.
1335         *
1336         * @param enumOptionKeys the list of Strings
1337         */
1338        public void setEnumOptionKeys(List<String> enumOptionKeys) {
1339            this.enumOptionKeys = enumOptionKeys;
1340        }
1341
1342        /**
1343         * Gets the enum option key.
1344         *
1345         * @return the enum option key
1346         */
1347        public String getEnumOptionKey() {
1348            return this.enumOptionKey;
1349        }
1350
1351        /**
1352         * Sets the enum option key.
1353         *
1354         * @param enumOptionKey the enum option key
1355         */
1356        public void setEnumOptionKey(String enumOptionKey) {
1357            this.enumOptionKey = enumOptionKey;
1358        }
1359
1360        /**
1361         * Gets the multi-select option key.
1362         *
1363         * @return the key.
1364         */
1365        public String getMultiSelectOptionKey() {
1366            return this.multiSelectOptionKey;
1367        }
1368
1369        /**
1370         * Sets the multi-select option key.
1371         *
1372         * @param key the key.
1373         */
1374        public void setMultiSelectOptionKey(String key) {
1375            this.multiSelectOptionKey = key;
1376        }
1377
1378        /**
1379         * Gets the list of multiselect option keys.
1380         *
1381         * @return the list of keys.
1382         */
1383        public List<String> getMultiSelectOptionKeys() {
1384            return this.multiSelectOptionKeys;
1385        }
1386
1387        /**
1388         * Sets the multi-select option keys.
1389         *
1390         * @param keys the list of keys.
1391         */
1392        public void setMultiSelectOptionKeys(List<String> keys) {
1393            this.multiSelectOptionKeys = keys;
1394        }
1395
1396        @Override
1397        public void clearPendingChanges() {
1398            super.clearPendingChanges();
1399        }
1400
1401        /**
1402         * {@inheritDoc}
1403         */
1404        @Override
1405        void parseJSONMember(JsonObject.Member member) {
1406            JsonValue value = member.getValue();
1407            String memberName = member.getName();
1408            switch (memberName) {
1409                case "op":
1410                    this.op = Operation.valueOf(value.asString());
1411                    break;
1412                case "data":
1413                    this.data = new Field(value.asObject());
1414                    break;
1415                case "fieldKey":
1416                    this.fieldKey = value.asString();
1417                    break;
1418                case "fieldKeys":
1419                    if (this.fieldKeys == null) {
1420                        this.fieldKeys = new ArrayList<>();
1421                    } else {
1422                        this.fieldKeys.clear();
1423                    }
1424                    for (JsonValue jsonValue : value.asArray()) {
1425                        this.fieldKeys.add(jsonValue.asString());
1426                    }
1427                    break;
1428                case "enumOptionKeys":
1429                    if (this.enumOptionKeys == null) {
1430                        this.enumOptionKeys = new ArrayList<>();
1431                    } else {
1432                        this.enumOptionKeys.clear();
1433                    }
1434
1435                    for (JsonValue jsonValue : value.asArray()) {
1436                        this.enumOptionKeys.add(jsonValue.asString());
1437                    }
1438                    break;
1439                case "enumOptionKey":
1440                    this.enumOptionKey = value.asString();
1441                    break;
1442                case "multiSelectOptionKey":
1443                    this.multiSelectOptionKey = value.asString();
1444                    break;
1445                case "multiSelectOptionKeys":
1446                    this.multiSelectOptionKeys = new ArrayList<>();
1447                    for (JsonValue key : value.asArray()) {
1448                        this.multiSelectOptionKeys.add(key.asString());
1449                    }
1450                    break;
1451                default:
1452                    break;
1453            }
1454        }
1455    }
1456}