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