001package com.box.sdk;
002
003import java.io.IOException;
004import java.io.InputStream;
005import java.net.URL;
006import java.util.ArrayList;
007import java.util.Collection;
008import java.util.Date;
009import java.util.EnumSet;
010import java.util.Iterator;
011import java.util.Map;
012import java.util.concurrent.TimeUnit;
013
014import com.box.sdk.internal.utils.Parsers;
015import com.eclipsesource.json.JsonArray;
016import com.eclipsesource.json.JsonObject;
017import com.eclipsesource.json.JsonValue;
018
019/**
020 * Represents a folder on Box. This class can be used to iterate through a folder's contents, collaborate a folder with
021 * another user or group, and perform other common folder operations (move, copy, delete, etc.).
022 * <p>
023 * <p>Unless otherwise noted, the methods in this class can throw an unchecked {@link BoxAPIException} (unchecked
024 * meaning that the compiler won't force you to handle it) if an error occurs. If you wish to implement custom error
025 * handling for errors related to the Box REST API, you should capture this exception explicitly.</p>
026 */
027@BoxResourceType("folder")
028public class BoxFolder extends BoxItem implements Iterable<BoxItem.Info> {
029    /**
030     * An array of all possible folder fields that can be requested when calling {@link #getInfo()}.
031     */
032    public static final String[] ALL_FIELDS = {"type", "id", "sequence_id", "etag", "name", "created_at", "modified_at",
033        "description", "size", "path_collection", "created_by", "modified_by", "trashed_at", "purged_at",
034        "content_created_at", "content_modified_at", "owned_by", "shared_link", "folder_upload_email", "parent",
035        "item_status", "item_collection", "sync_state", "has_collaborations", "permissions", "tags",
036        "can_non_owners_invite", "collections", "watermark_info", "metadata"};
037
038    /**
039     * Used to specify what direction to sort and display results.
040     */
041    public enum SortDirection {
042        /**
043         * ASC for ascending order.
044         */
045        ASC,
046
047        /**
048         * DESC for descending order.
049         */
050        DESC
051    }
052
053    /**
054     * Create Folder URL Template.
055     */
056    public static final URLTemplate CREATE_FOLDER_URL = new URLTemplate("folders");
057    /**
058     * Create Web Link URL Template.
059     */
060    public static final URLTemplate CREATE_WEB_LINK_URL = new URLTemplate("web_links");
061    /**
062     * Copy Folder URL Template.
063     */
064    public static final URLTemplate COPY_FOLDER_URL = new URLTemplate("folders/%s/copy");
065    /**
066     * Delete Folder URL Template.
067     */
068    public static final URLTemplate DELETE_FOLDER_URL = new URLTemplate("folders/%s?recursive=%b");
069    /**
070     * Folder Info URL Template.
071     */
072    public static final URLTemplate FOLDER_INFO_URL_TEMPLATE = new URLTemplate("folders/%s");
073    /**
074     * Upload File URL Template.
075     */
076    public static final URLTemplate UPLOAD_FILE_URL = new URLTemplate("files/content");
077    /**
078     * Add Collaboration URL Template.
079     */
080    public static final URLTemplate ADD_COLLABORATION_URL = new URLTemplate("collaborations");
081    /**
082     * Get Collaborations URL Template.
083     */
084    public static final URLTemplate GET_COLLABORATIONS_URL = new URLTemplate("folders/%s/collaborations");
085    /**
086     * Get Items URL Template.
087     */
088    public static final URLTemplate GET_ITEMS_URL = new URLTemplate("folders/%s/items/");
089    /**
090     * Search URL Template.
091     */
092    public static final URLTemplate SEARCH_URL_TEMPLATE = new URLTemplate("search");
093    /**
094     * Metadata URL Template.
095     */
096    public static final URLTemplate METADATA_URL_TEMPLATE = new URLTemplate("folders/%s/metadata/%s/%s");
097    /**
098     * Upload Session URL Template.
099     */
100    public static final URLTemplate UPLOAD_SESSION_URL_TEMPLATE = new URLTemplate("files/upload_sessions");
101
102    /**
103     * Constructs a BoxFolder for a folder with a given ID.
104     *
105     * @param api the API connection to be used by the folder.
106     * @param id  the ID of the folder.
107     */
108    public BoxFolder(BoxAPIConnection api, String id) {
109        super(api, id);
110    }
111
112    /**
113     * {@inheritDoc}
114     */
115    @Override
116    protected URL getItemURL() {
117        return FOLDER_INFO_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
118    }
119
120    /**
121     * Gets the current user's root folder.
122     *
123     * @param api the API connection to be used by the folder.
124     * @return the user's root folder.
125     */
126    public static BoxFolder getRootFolder(BoxAPIConnection api) {
127        return new BoxFolder(api, "0");
128    }
129
130    /**
131     * Adds a collaborator to this folder.
132     *
133     * @param collaborator the collaborator to add.
134     * @param role         the role of the collaborator.
135     * @return info about the new collaboration.
136     */
137    public BoxCollaboration.Info collaborate(BoxCollaborator collaborator, BoxCollaboration.Role role) {
138        JsonObject accessibleByField = new JsonObject();
139        accessibleByField.add("id", collaborator.getID());
140
141        if (collaborator instanceof BoxUser) {
142            accessibleByField.add("type", "user");
143        } else if (collaborator instanceof BoxGroup) {
144            accessibleByField.add("type", "group");
145        } else {
146            throw new IllegalArgumentException("The given collaborator is of an unknown type.");
147        }
148
149        return this.collaborate(accessibleByField, role, null, null);
150    }
151
152    /**
153     * Adds a collaborator to this folder. An email will be sent to the collaborator if they don't already have a Box
154     * account.
155     *
156     * @param email the email address of the collaborator to add.
157     * @param role  the role of the collaborator.
158     * @return info about the new collaboration.
159     */
160    public BoxCollaboration.Info collaborate(String email, BoxCollaboration.Role role) {
161        JsonObject accessibleByField = new JsonObject();
162        accessibleByField.add("login", email);
163        accessibleByField.add("type", "user");
164
165        return this.collaborate(accessibleByField, role, null, null);
166    }
167
168    /**
169     * Adds a collaborator to this folder.
170     *
171     * @param collaborator the collaborator to add.
172     * @param role         the role of the collaborator.
173     * @param notify       the user/group should receive email notification of the collaboration or not.
174     * @param canViewPath  the view path collaboration feature is enabled or not.
175     *                     View path collaborations allow the invitee to see the entire ancestral path to the associated
176     *                     folder. The user will not gain privileges in any ancestral folder.
177     * @return info about the new collaboration.
178     */
179    public BoxCollaboration.Info collaborate(BoxCollaborator collaborator, BoxCollaboration.Role role,
180                                             Boolean notify, Boolean canViewPath) {
181        JsonObject accessibleByField = new JsonObject();
182        accessibleByField.add("id", collaborator.getID());
183
184        if (collaborator instanceof BoxUser) {
185            accessibleByField.add("type", "user");
186        } else if (collaborator instanceof BoxGroup) {
187            accessibleByField.add("type", "group");
188        } else {
189            throw new IllegalArgumentException("The given collaborator is of an unknown type.");
190        }
191
192        return this.collaborate(accessibleByField, role, notify, canViewPath);
193    }
194
195    /**
196     * Adds a collaborator to this folder. An email will be sent to the collaborator if they don't already have a Box
197     * account.
198     *
199     * @param email       the email address of the collaborator to add.
200     * @param role        the role of the collaborator.
201     * @param notify      the user/group should receive email notification of the collaboration or not.
202     * @param canViewPath the view path collaboration feature is enabled or not.
203     *                    View path collaborations allow the invitee to see the entire ancestral path to the associated
204     *                    folder. The user will not gain privileges in any ancestral folder.
205     * @return info about the new collaboration.
206     */
207    public BoxCollaboration.Info collaborate(String email, BoxCollaboration.Role role,
208                                             Boolean notify, Boolean canViewPath) {
209        JsonObject accessibleByField = new JsonObject();
210        accessibleByField.add("login", email);
211        accessibleByField.add("type", "user");
212
213        return this.collaborate(accessibleByField, role, notify, canViewPath);
214    }
215
216    private BoxCollaboration.Info collaborate(JsonObject accessibleByField, BoxCollaboration.Role role,
217                                              Boolean notify, Boolean canViewPath) {
218
219        JsonObject itemField = new JsonObject();
220        itemField.add("id", this.getID());
221        itemField.add("type", "folder");
222
223        return BoxCollaboration.create(this.getAPI(), accessibleByField, itemField, role, notify, canViewPath);
224    }
225
226    @Override
227    public BoxSharedLink createSharedLink(BoxSharedLink.Access access, Date unshareDate,
228                                          BoxSharedLink.Permissions permissions) {
229
230        BoxSharedLink sharedLink = new BoxSharedLink(access, unshareDate, permissions);
231        Info info = new Info();
232        info.setSharedLink(sharedLink);
233
234        this.updateInfo(info);
235        return info.getSharedLink();
236    }
237
238    /**
239     * Creates new SharedLink for a BoxFolder with a password.
240     *
241     * @param access        The access level of the shared link.
242     * @param unshareDate   A specified date to unshare the Box folder.
243     * @param permissions   The permissions to set on the shared link for the Box folder.
244     * @param password      Password set on the shared link to give access to the Box folder.
245     * @return information about the newly created shared link.
246     */
247    public BoxSharedLink createSharedLink(BoxSharedLink.Access access, Date unshareDate,
248                                          BoxSharedLink.Permissions permissions, String password) {
249
250        BoxSharedLink sharedLink = new BoxSharedLink(access, unshareDate, permissions, password);
251        Info info = new Info();
252        info.setSharedLink(sharedLink);
253
254        this.updateInfo(info);
255        return info.getSharedLink();
256    }
257
258    /**
259     * Gets information about all of the collaborations for this folder.
260     *
261     * @return a collection of information about the collaborations for this folder.
262     */
263    public Collection<BoxCollaboration.Info> getCollaborations() {
264        BoxAPIConnection api = this.getAPI();
265        URL url = GET_COLLABORATIONS_URL.build(api.getBaseURL(), this.getID());
266
267        BoxAPIRequest request = new BoxAPIRequest(api, url, "GET");
268        BoxJSONResponse response = (BoxJSONResponse) request.send();
269        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
270
271        int entriesCount = responseJSON.get("total_count").asInt();
272        Collection<BoxCollaboration.Info> collaborations = new ArrayList<BoxCollaboration.Info>(entriesCount);
273        JsonArray entries = responseJSON.get("entries").asArray();
274        for (JsonValue entry : entries) {
275            JsonObject entryObject = entry.asObject();
276            BoxCollaboration collaboration = new BoxCollaboration(api, entryObject.get("id").asString());
277            BoxCollaboration.Info info = collaboration.new Info(entryObject);
278            collaborations.add(info);
279        }
280
281        return collaborations;
282    }
283
284    @Override
285    public BoxFolder.Info getInfo() {
286        URL url = FOLDER_INFO_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
287        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET");
288        BoxJSONResponse response = (BoxJSONResponse) request.send();
289        return new Info(response.getJSON());
290    }
291
292    @Override
293    public BoxFolder.Info getInfo(String... fields) {
294        String queryString = new QueryStringBuilder().appendParam("fields", fields).toString();
295        URL url = FOLDER_INFO_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), queryString, this.getID());
296
297        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET");
298        BoxJSONResponse response = (BoxJSONResponse) request.send();
299        return new Info(response.getJSON());
300    }
301
302    /**
303     * Updates the information about this folder with any info fields that have been modified locally.
304     *
305     * @param info the updated info.
306     */
307    public void updateInfo(BoxFolder.Info info) {
308        URL url = FOLDER_INFO_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
309        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT");
310        request.setBody(info.getPendingChanges());
311        BoxJSONResponse response = (BoxJSONResponse) request.send();
312        JsonObject jsonObject = JsonObject.readFrom(response.getJSON());
313        info.update(jsonObject);
314    }
315
316    @Override
317    public BoxFolder.Info copy(BoxFolder destination) {
318        return this.copy(destination, null);
319    }
320
321    @Override
322    public BoxFolder.Info copy(BoxFolder destination, String newName) {
323        URL url = COPY_FOLDER_URL.build(this.getAPI().getBaseURL(), this.getID());
324        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "POST");
325
326        JsonObject parent = new JsonObject();
327        parent.add("id", destination.getID());
328
329        JsonObject copyInfo = new JsonObject();
330        copyInfo.add("parent", parent);
331        if (newName != null) {
332            copyInfo.add("name", newName);
333        }
334
335        request.setBody(copyInfo.toString());
336        BoxJSONResponse response = (BoxJSONResponse) request.send();
337        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
338        BoxFolder copiedFolder = new BoxFolder(this.getAPI(), responseJSON.get("id").asString());
339        return copiedFolder.new Info(responseJSON);
340    }
341
342    /**
343     * Creates a new child folder inside this folder.
344     *
345     * @param name the new folder's name.
346     * @return the created folder's info.
347     */
348    public BoxFolder.Info createFolder(String name) {
349        JsonObject parent = new JsonObject();
350        parent.add("id", this.getID());
351
352        JsonObject newFolder = new JsonObject();
353        newFolder.add("name", name);
354        newFolder.add("parent", parent);
355
356        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), CREATE_FOLDER_URL.build(this.getAPI().getBaseURL()),
357                "POST");
358        request.setBody(newFolder.toString());
359        BoxJSONResponse response = (BoxJSONResponse) request.send();
360        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
361
362        BoxFolder createdFolder = new BoxFolder(this.getAPI(), responseJSON.get("id").asString());
363        return createdFolder.new Info(responseJSON);
364    }
365
366    /**
367     * Deletes this folder, optionally recursively deleting all of its contents.
368     *
369     * @param recursive true to recursively delete this folder's contents; otherwise false.
370     */
371    public void delete(boolean recursive) {
372        URL url = DELETE_FOLDER_URL.build(this.getAPI().getBaseURL(), this.getID(), recursive);
373        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "DELETE");
374        BoxAPIResponse response = request.send();
375        response.disconnect();
376    }
377
378    @Override
379    public BoxItem.Info move(BoxFolder destination) {
380        return this.move(destination, null);
381    }
382
383    @Override
384    public BoxItem.Info move(BoxFolder destination, String newName) {
385        URL url = FOLDER_INFO_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
386        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT");
387
388        JsonObject parent = new JsonObject();
389        parent.add("id", destination.getID());
390
391        JsonObject updateInfo = new JsonObject();
392        updateInfo.add("parent", parent);
393        if (newName != null) {
394            updateInfo.add("name", newName);
395        }
396
397        request.setBody(updateInfo.toString());
398        BoxJSONResponse response = (BoxJSONResponse) request.send();
399        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
400        BoxFolder movedFolder = new BoxFolder(this.getAPI(), responseJSON.get("id").asString());
401        return movedFolder.new Info(responseJSON);
402    }
403
404    /**
405     * Renames this folder.
406     *
407     * @param newName the new name of the folder.
408     */
409    public void rename(String newName) {
410        URL url = FOLDER_INFO_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
411        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT");
412
413        JsonObject updateInfo = new JsonObject();
414        updateInfo.add("name", newName);
415
416        request.setBody(updateInfo.toString());
417        BoxJSONResponse response = (BoxJSONResponse) request.send();
418        response.getJSON();
419    }
420
421    /**
422     * Checks if the file can be successfully uploaded by using the preflight check.
423     *
424     * @param name     the name to give the uploaded file.
425     * @param fileSize the size of the file used for account capacity calculations.
426     */
427    public void canUpload(String name, long fileSize) {
428        URL url = UPLOAD_FILE_URL.build(this.getAPI().getBaseURL());
429        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "OPTIONS");
430
431        JsonObject parent = new JsonObject();
432        parent.add("id", this.getID());
433
434        JsonObject preflightInfo = new JsonObject();
435        preflightInfo.add("parent", parent);
436        preflightInfo.add("name", name);
437
438        preflightInfo.add("size", fileSize);
439
440        request.setBody(preflightInfo.toString());
441        BoxAPIResponse response = request.send();
442        response.disconnect();
443    }
444
445    /**
446     * Uploads a new file to this folder.
447     *
448     * @param fileContent a stream containing the contents of the file to upload.
449     * @param name        the name to give the uploaded file.
450     * @return the uploaded file's info.
451     */
452    public BoxFile.Info uploadFile(InputStream fileContent, String name) {
453        FileUploadParams uploadInfo = new FileUploadParams()
454                .setContent(fileContent)
455                .setName(name);
456        return this.uploadFile(uploadInfo);
457    }
458
459    /**
460     * Uploads a new file to this folder.
461     *
462     * @param callback the callback which allows file content to be written on output stream.
463     * @param name     the name to give the uploaded file.
464     * @return the uploaded file's info.
465     */
466    public BoxFile.Info uploadFile(UploadFileCallback callback, String name) {
467        FileUploadParams uploadInfo = new FileUploadParams()
468                .setUploadFileCallback(callback)
469                .setName(name);
470        return this.uploadFile(uploadInfo);
471    }
472
473    /**
474     * Uploads a new file to this folder while reporting the progress to a ProgressListener.
475     *
476     * @param fileContent a stream containing the contents of the file to upload.
477     * @param name        the name to give the uploaded file.
478     * @param fileSize    the size of the file used for determining the progress of the upload.
479     * @param listener    a listener for monitoring the upload's progress.
480     * @return the uploaded file's info.
481     */
482    public BoxFile.Info uploadFile(InputStream fileContent, String name, long fileSize, ProgressListener listener) {
483        FileUploadParams uploadInfo = new FileUploadParams()
484                .setContent(fileContent)
485                .setName(name)
486                .setSize(fileSize)
487                .setProgressListener(listener);
488        return this.uploadFile(uploadInfo);
489    }
490
491    /**
492     * Uploads a new file to this folder with a specified file description.
493     *
494     * @param fileContent a stream containing the contents of the file to upload.
495     * @param name        the name to give the uploaded file.
496     * @param description the description to give the uploaded file.
497     * @return the uploaded file's info.
498     */
499    public BoxFile.Info uploadFile(InputStream fileContent, String name, String description) {
500        FileUploadParams uploadInfo = new FileUploadParams()
501                .setContent(fileContent)
502                .setName(name)
503                .setDescription(description);
504        return this.uploadFile(uploadInfo);
505    }
506
507    /**
508     * Uploads a new file to this folder with custom upload parameters.
509     *
510     * @param uploadParams the custom upload parameters.
511     * @return the uploaded file's info.
512     */
513    public BoxFile.Info uploadFile(FileUploadParams uploadParams) {
514        URL uploadURL = UPLOAD_FILE_URL.build(this.getAPI().getBaseUploadURL());
515        BoxMultipartRequest request = new BoxMultipartRequest(getAPI(), uploadURL);
516
517        JsonObject fieldJSON = new JsonObject();
518        JsonObject parentIdJSON = new JsonObject();
519        parentIdJSON.add("id", getID());
520        fieldJSON.add("name", uploadParams.getName());
521        fieldJSON.add("parent", parentIdJSON);
522
523        if (uploadParams.getCreated() != null) {
524            fieldJSON.add("content_created_at", BoxDateFormat.format(uploadParams.getCreated()));
525        }
526
527        if (uploadParams.getModified() != null) {
528            fieldJSON.add("content_modified_at", BoxDateFormat.format(uploadParams.getModified()));
529        }
530
531        if (uploadParams.getSHA1() != null && !uploadParams.getSHA1().isEmpty()) {
532            request.setContentSHA1(uploadParams.getSHA1());
533        }
534
535        if (uploadParams.getDescription() != null) {
536            fieldJSON.add("description", uploadParams.getDescription());
537        }
538
539        request.putField("attributes", fieldJSON.toString());
540
541        if (uploadParams.getSize() > 0) {
542            request.setFile(uploadParams.getContent(), uploadParams.getName(), uploadParams.getSize());
543        } else if (uploadParams.getContent() != null) {
544            request.setFile(uploadParams.getContent(), uploadParams.getName());
545        } else {
546            request.setUploadFileCallback(uploadParams.getUploadFileCallback(), uploadParams.getName());
547        }
548
549        BoxJSONResponse response;
550        if (uploadParams.getProgressListener() == null) {
551            response = (BoxJSONResponse) request.send();
552        } else {
553            response = (BoxJSONResponse) request.send(uploadParams.getProgressListener());
554        }
555        JsonObject collection = JsonObject.readFrom(response.getJSON());
556        JsonArray entries = collection.get("entries").asArray();
557        JsonObject fileInfoJSON = entries.get(0).asObject();
558        String uploadedFileID = fileInfoJSON.get("id").asString();
559
560        BoxFile uploadedFile = new BoxFile(getAPI(), uploadedFileID);
561        return uploadedFile.new Info(fileInfoJSON);
562    }
563
564    /**
565     * Uploads a new weblink to this folder.
566     *
567     * @param linkURL the URL the weblink points to.
568     * @return the uploaded weblink's info.
569     */
570    public BoxWebLink.Info createWebLink(URL linkURL) {
571        return this.createWebLink(null, linkURL,
572                null);
573    }
574
575    /**
576     * Uploads a new weblink to this folder.
577     *
578     * @param name    the filename for the weblink.
579     * @param linkURL the URL the weblink points to.
580     * @return the uploaded weblink's info.
581     */
582    public BoxWebLink.Info createWebLink(String name, URL linkURL) {
583        return this.createWebLink(name, linkURL,
584                null);
585    }
586
587    /**
588     * Uploads a new weblink to this folder.
589     *
590     * @param linkURL     the URL the weblink points to.
591     * @param description the weblink's description.
592     * @return the uploaded weblink's info.
593     */
594    public BoxWebLink.Info createWebLink(URL linkURL, String description) {
595        return this.createWebLink(null, linkURL, description);
596    }
597
598    /**
599     * Uploads a new weblink to this folder.
600     *
601     * @param name        the filename for the weblink.
602     * @param linkURL     the URL the weblink points to.
603     * @param description the weblink's description.
604     * @return the uploaded weblink's info.
605     */
606    public BoxWebLink.Info createWebLink(String name, URL linkURL, String description) {
607        JsonObject parent = new JsonObject();
608        parent.add("id", this.getID());
609
610        JsonObject newWebLink = new JsonObject();
611        newWebLink.add("name", name);
612        newWebLink.add("parent", parent);
613        newWebLink.add("url", linkURL.toString());
614
615        if (description != null) {
616            newWebLink.add("description", description);
617        }
618
619        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(),
620                CREATE_WEB_LINK_URL.build(this.getAPI().getBaseURL()), "POST");
621        request.setBody(newWebLink.toString());
622        BoxJSONResponse response = (BoxJSONResponse) request.send();
623        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
624
625        BoxWebLink createdWebLink = new BoxWebLink(this.getAPI(), responseJSON.get("id").asString());
626        return createdWebLink.new Info(responseJSON);
627    }
628
629    /**
630     * Returns an iterable containing the items in this folder. Iterating over the iterable returned by this method is
631     * equivalent to iterating over this BoxFolder directly.
632     *
633     * @return an iterable containing the items in this folder.
634     */
635    public Iterable<BoxItem.Info> getChildren() {
636        return this;
637    }
638
639    /**
640     * Returns an iterable containing the items in this folder and specifies which child fields to retrieve from the
641     * API.
642     *
643     * @param fields the fields to retrieve.
644     * @return an iterable containing the items in this folder.
645     */
646    public Iterable<BoxItem.Info> getChildren(final String... fields) {
647        return new Iterable<BoxItem.Info>() {
648            @Override
649            public Iterator<BoxItem.Info> iterator() {
650                String queryString = new QueryStringBuilder().appendParam("fields", fields).toString();
651                URL url = GET_ITEMS_URL.buildWithQuery(getAPI().getBaseURL(), queryString, getID());
652                return new BoxItemIterator(getAPI(), url);
653            }
654        };
655    }
656
657    /**
658     * Returns an iterable containing the items in this folder sorted by name and direction.
659     * @param sort the field to sort by, can be set as `name`, `id`, and `date`.
660     * @param direction the direction to display the item results.
661     * @param fields the fields to retrieve.
662     * @return an iterable containing the items in this folder.
663     */
664    public Iterable<BoxItem.Info> getChildren(String sort, SortDirection direction, final String... fields) {
665        QueryStringBuilder builder = new QueryStringBuilder()
666                .appendParam("sort", sort)
667                .appendParam("direction", direction.toString());
668
669        if (fields.length > 0) {
670            builder.appendParam("fields", fields).toString();
671        }
672        final String query = builder.toString();
673        return new Iterable<BoxItem.Info>() {
674            @Override
675            public Iterator<BoxItem.Info> iterator() {
676                URL url = GET_ITEMS_URL.buildWithQuery(getAPI().getBaseURL(), query, getID());
677                return new BoxItemIterator(getAPI(), url);
678            }
679        };
680    }
681
682    /**
683     * Retrieves a specific range of child items in this folder.
684     *
685     * @param offset the index of the first child item to retrieve.
686     * @param limit  the maximum number of children to retrieve after the offset.
687     * @param fields the fields to retrieve.
688     * @return a partial collection containing the specified range of child items.
689     */
690    public PartialCollection<BoxItem.Info> getChildrenRange(long offset, long limit, String... fields) {
691        QueryStringBuilder builder = new QueryStringBuilder()
692                .appendParam("limit", limit)
693                .appendParam("offset", offset);
694
695        if (fields.length > 0) {
696            builder.appendParam("fields", fields).toString();
697        }
698
699        URL url = GET_ITEMS_URL.buildWithQuery(getAPI().getBaseURL(), builder.toString(), getID());
700        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET");
701        BoxJSONResponse response = (BoxJSONResponse) request.send();
702        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
703
704        String totalCountString = responseJSON.get("total_count").toString();
705        long fullSize = Double.valueOf(totalCountString).longValue();
706        PartialCollection<BoxItem.Info> children = new PartialCollection<BoxItem.Info>(offset, limit, fullSize);
707        JsonArray jsonArray = responseJSON.get("entries").asArray();
708        for (JsonValue value : jsonArray) {
709            JsonObject jsonObject = value.asObject();
710            BoxItem.Info parsedItemInfo = (BoxItem.Info) BoxResource.parseInfo(this.getAPI(), jsonObject);
711            if (parsedItemInfo != null) {
712                children.add(parsedItemInfo);
713            }
714        }
715        return children;
716    }
717
718    /**
719     * Returns an iterator over the items in this folder.
720     *
721     * @return an iterator over the items in this folder.
722     */
723    @Override
724    public Iterator<BoxItem.Info> iterator() {
725        URL url = GET_ITEMS_URL.build(this.getAPI().getBaseURL(), BoxFolder.this.getID());
726        return new BoxItemIterator(BoxFolder.this.getAPI(), url);
727    }
728
729    /**
730     * Adds new {@link BoxWebHook} to this {@link BoxFolder}.
731     *
732     * @param address  {@link BoxWebHook.Info#getAddress()}
733     * @param triggers {@link BoxWebHook.Info#getTriggers()}
734     * @return created {@link BoxWebHook.Info}
735     */
736    public BoxWebHook.Info addWebHook(URL address, BoxWebHook.Trigger... triggers) {
737        return BoxWebHook.create(this, address, triggers);
738    }
739
740    /**
741     * Used to retrieve the watermark for the folder.
742     * If the folder does not have a watermark applied to it, a 404 Not Found will be returned by API.
743     *
744     * @param fields the fields to retrieve.
745     * @return the watermark associated with the folder.
746     */
747    public BoxWatermark getWatermark(String... fields) {
748        return this.getWatermark(FOLDER_INFO_URL_TEMPLATE, fields);
749    }
750
751    /**
752     * Used to apply or update the watermark for the folder.
753     *
754     * @return the watermark associated with the folder.
755     */
756    public BoxWatermark applyWatermark() {
757        return this.applyWatermark(FOLDER_INFO_URL_TEMPLATE, BoxWatermark.WATERMARK_DEFAULT_IMPRINT);
758    }
759
760    /**
761     * Removes a watermark from the folder.
762     * If the folder did not have a watermark applied to it, a 404 Not Found will be returned by API.
763     */
764    public void removeWatermark() {
765        this.removeWatermark(FOLDER_INFO_URL_TEMPLATE);
766    }
767
768    /**
769     * Used to retrieve all metadata associated with the folder.
770     *
771     * @param fields the optional fields to retrieve.
772     * @return An iterable of metadata instances associated with the folder
773     */
774    public Iterable<Metadata> getAllMetadata(String... fields) {
775        return Metadata.getAllMetadata(this, fields);
776    }
777
778    /**
779     * This method is deprecated, please use the {@link BoxSearch} class instead.
780     * Searches this folder and all descendant folders using a given queryPlease use BoxSearch Instead.
781     *
782     * @param query the search query.
783     * @return an Iterable containing the search results.
784     */
785    @Deprecated
786    public Iterable<BoxItem.Info> search(final String query) {
787        return new Iterable<BoxItem.Info>() {
788            @Override
789            public Iterator<BoxItem.Info> iterator() {
790                QueryStringBuilder builder = new QueryStringBuilder();
791                builder.appendParam("query", query);
792                builder.appendParam("ancestor_folder_ids", getID());
793
794                URL url = SEARCH_URL_TEMPLATE.buildWithQuery(getAPI().getBaseURL(), builder.toString());
795                return new BoxItemIterator(getAPI(), url);
796            }
797        };
798    }
799
800    @Override
801    public BoxFolder.Info setCollections(BoxCollection... collections) {
802        JsonArray jsonArray = new JsonArray();
803        for (BoxCollection collection : collections) {
804            JsonObject collectionJSON = new JsonObject();
805            collectionJSON.add("id", collection.getID());
806            jsonArray.add(collectionJSON);
807        }
808        JsonObject infoJSON = new JsonObject();
809        infoJSON.add("collections", jsonArray);
810
811        String queryString = new QueryStringBuilder().appendParam("fields", ALL_FIELDS).toString();
812        URL url = FOLDER_INFO_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), queryString, this.getID());
813        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT");
814        request.setBody(infoJSON.toString());
815        BoxJSONResponse response = (BoxJSONResponse) request.send();
816        JsonObject jsonObject = JsonObject.readFrom(response.getJSON());
817        return new Info(jsonObject);
818    }
819
820    /**
821     * Creates global property metadata on this folder.
822     *
823     * @param metadata the new metadata values.
824     * @return the metadata returned from the server.
825     */
826    public Metadata createMetadata(Metadata metadata) {
827        return this.createMetadata(Metadata.DEFAULT_METADATA_TYPE, metadata);
828    }
829
830    /**
831     * Creates metadata on this folder using a specified template.
832     *
833     * @param templateName the name of the metadata template.
834     * @param metadata     the new metadata values.
835     * @return the metadata returned from the server.
836     */
837    public Metadata createMetadata(String templateName, Metadata metadata) {
838        String scope = Metadata.scopeBasedOnType(templateName);
839        return this.createMetadata(templateName, scope, metadata);
840    }
841
842    /**
843     * Creates metadata on this folder using a specified scope and template.
844     *
845     * @param templateName the name of the metadata template.
846     * @param scope        the scope of the template (usually "global" or "enterprise").
847     * @param metadata     the new metadata values.
848     * @return the metadata returned from the server.
849     */
850    public Metadata createMetadata(String templateName, String scope, Metadata metadata) {
851        URL url = METADATA_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), scope, templateName);
852        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "POST");
853        request.addHeader("Content-Type", "application/json");
854        request.setBody(metadata.toString());
855        BoxJSONResponse response = (BoxJSONResponse) request.send();
856        return new Metadata(JsonObject.readFrom(response.getJSON()));
857    }
858
859    /**
860     * Sets the provided metadata on the folder, overwriting any existing metadata keys already present.
861     *
862     * @param templateName the name of the metadata template.
863     * @param scope        the scope of the template (usually "global" or "enterprise").
864     * @param metadata     the new metadata values.
865     * @return the metadata returned from the server.
866     */
867    public Metadata setMetadata(String templateName, String scope, Metadata metadata) {
868        Metadata metadataValue = null;
869
870        try {
871            metadataValue = this.createMetadata(templateName, scope, metadata);
872        } catch (BoxAPIException e) {
873            if (e.getResponseCode() == 409) {
874                Metadata metadataToUpdate = new Metadata(scope, templateName);
875                for (JsonValue value : metadata.getOperations()) {
876                    if (value.asObject().get("value").isNumber()) {
877                        metadataToUpdate.add(value.asObject().get("path").asString(),
878                                value.asObject().get("value").asFloat());
879                    } else if (value.asObject().get("value").isString()) {
880                        metadataToUpdate.add(value.asObject().get("path").asString(),
881                                value.asObject().get("value").asString());
882                    } else if (value.asObject().get("value").isArray()) {
883                        ArrayList<String> list = new ArrayList<String>();
884                        for (JsonValue jsonValue : value.asObject().get("value").asArray()) {
885                            list.add(jsonValue.asString());
886                        }
887                        metadataToUpdate.add(value.asObject().get("path").asString(), list);
888                    }
889                }
890                metadataValue = this.updateMetadata(metadataToUpdate);
891            }
892        }
893
894        return metadataValue;
895    }
896
897    /**
898     * Gets the global properties metadata on this folder.
899     *
900     * @return the metadata returned from the server.
901     */
902    public Metadata getMetadata() {
903        return this.getMetadata(Metadata.DEFAULT_METADATA_TYPE);
904    }
905
906    /**
907     * Gets the metadata on this folder associated with a specified template.
908     *
909     * @param templateName the metadata template type name.
910     * @return the metadata returned from the server.
911     */
912    public Metadata getMetadata(String templateName) {
913        String scope = Metadata.scopeBasedOnType(templateName);
914        return this.getMetadata(templateName, scope);
915    }
916
917    /**
918     * Gets the metadata on this folder associated with a specified scope and template.
919     *
920     * @param templateName the metadata template type name.
921     * @param scope        the scope of the template (usually "global" or "enterprise").
922     * @return the metadata returned from the server.
923     */
924    public Metadata getMetadata(String templateName, String scope) {
925        URL url = METADATA_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), scope, templateName);
926        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET");
927        BoxJSONResponse response = (BoxJSONResponse) request.send();
928        return new Metadata(JsonObject.readFrom(response.getJSON()));
929    }
930
931    /**
932     * Updates the global properties metadata on this folder.
933     *
934     * @param metadata the new metadata values.
935     * @return the metadata returned from the server.
936     */
937    public Metadata updateMetadata(Metadata metadata) {
938        URL url = METADATA_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), metadata.getScope(),
939                metadata.getTemplateName());
940        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "PUT");
941        request.addHeader("Content-Type", "application/json-patch+json");
942        request.setBody(metadata.getPatch());
943        BoxJSONResponse response = (BoxJSONResponse) request.send();
944        return new Metadata(JsonObject.readFrom(response.getJSON()));
945    }
946
947    /**
948     * Deletes the global properties metadata on this folder.
949     */
950    public void deleteMetadata() {
951        this.deleteMetadata(Metadata.DEFAULT_METADATA_TYPE);
952    }
953
954    /**
955     * Deletes the metadata on this folder associated with a specified template.
956     *
957     * @param templateName the metadata template type name.
958     */
959    public void deleteMetadata(String templateName) {
960        String scope = Metadata.scopeBasedOnType(templateName);
961        this.deleteMetadata(templateName, scope);
962    }
963
964    /**
965     * Deletes the metadata on this folder associated with a specified scope and template.
966     *
967     * @param templateName the metadata template type name.
968     * @param scope        the scope of the template (usually "global" or "enterprise").
969     */
970    public void deleteMetadata(String templateName, String scope) {
971        URL url = METADATA_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), scope, templateName);
972        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "DELETE");
973        BoxAPIResponse response = request.send();
974        response.disconnect();
975    }
976
977    /**
978     * Adds a metadata classification to the specified file.
979     *
980     * @param classificationType the metadata classification type.
981     * @return the metadata classification type added to the file.
982     */
983    public String addClassification(String classificationType) {
984        Metadata metadata = new Metadata().add(Metadata.CLASSIFICATION_KEY, classificationType);
985        Metadata classification = this.createMetadata(Metadata.CLASSIFICATION_TEMPLATE_KEY,
986                "enterprise", metadata);
987
988        return classification.getString(Metadata.CLASSIFICATION_KEY);
989    }
990
991    /**
992     * Updates a metadata classification on the specified file.
993     *
994     * @param classificationType the metadata classification type.
995     * @return the new metadata classification type updated on the file.
996     */
997    public String updateClassification(String classificationType) {
998        Metadata metadata = new Metadata("enterprise", Metadata.CLASSIFICATION_TEMPLATE_KEY);
999        metadata.replace(Metadata.CLASSIFICATION_KEY, classificationType);
1000        Metadata classification = this.updateMetadata(metadata);
1001
1002        return classification.getString(Metadata.CLASSIFICATION_KEY);
1003    }
1004
1005    /**
1006     * Attempts to add classification to a file. If classification already exists then do update.
1007     *
1008     * @param classificationType the metadata classification type.
1009     * @return the metadata classification type on the file.
1010     */
1011    public String setClassification(String classificationType) {
1012        Metadata metadata = new Metadata().add(Metadata.CLASSIFICATION_KEY, classificationType);
1013        Metadata classification = null;
1014
1015        try {
1016            classification = this.createMetadata(Metadata.CLASSIFICATION_TEMPLATE_KEY, "enterprise", metadata);
1017        } catch (BoxAPIException e) {
1018            if (e.getResponseCode() == 409) {
1019                metadata = new Metadata("enterprise", Metadata.CLASSIFICATION_TEMPLATE_KEY);
1020                metadata.replace(Metadata.CLASSIFICATION_KEY, classificationType);
1021                classification = this.updateMetadata(metadata);
1022            } else {
1023                throw e;
1024            }
1025        }
1026
1027        return classification.getString("/Box__Security__Classification__Key");
1028    }
1029
1030    /**
1031     * Gets the classification type for the specified file.
1032     *
1033     * @return the metadata classification type on the file.
1034     */
1035    public String getClassification() {
1036        Metadata metadata = this.getMetadata(Metadata.CLASSIFICATION_TEMPLATE_KEY);
1037        return metadata.getString(Metadata.CLASSIFICATION_KEY);
1038    }
1039
1040    /**
1041     * Deletes the classification on the file.
1042     */
1043    public void deleteClassification() {
1044        this.deleteMetadata(Metadata.CLASSIFICATION_TEMPLATE_KEY, "enterprise");
1045    }
1046
1047    /**
1048     * Creates an upload session to create a new file in chunks.
1049     * This will first verify that the file can be created and then open a session for uploading pieces of the file.
1050     *
1051     * @param fileName the name of the file to be created
1052     * @param fileSize the size of the file that will be uploaded
1053     * @return the created upload session instance
1054     */
1055    public BoxFileUploadSession.Info createUploadSession(String fileName, long fileSize) {
1056
1057        URL url = UPLOAD_SESSION_URL_TEMPLATE.build(this.getAPI().getBaseUploadURL());
1058        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "POST");
1059
1060        JsonObject body = new JsonObject();
1061        body.add("folder_id", this.getID());
1062        body.add("file_name", fileName);
1063        body.add("file_size", fileSize);
1064        request.setBody(body.toString());
1065
1066        BoxJSONResponse response = (BoxJSONResponse) request.send();
1067        JsonObject jsonObject = JsonObject.readFrom(response.getJSON());
1068
1069        String sessionId = jsonObject.get("id").asString();
1070        BoxFileUploadSession session = new BoxFileUploadSession(this.getAPI(), sessionId);
1071
1072        return session.new Info(jsonObject);
1073    }
1074
1075    /**
1076     * Creates a new file.
1077     *
1078     * @param inputStream the stream instance that contains the data.
1079     * @param fileName    the name of the file to be created.
1080     * @param fileSize    the size of the file that will be uploaded.
1081     * @return the created file instance.
1082     * @throws InterruptedException when a thread execution is interrupted.
1083     * @throws IOException          when reading a stream throws exception.
1084     */
1085    public BoxFile.Info uploadLargeFile(InputStream inputStream, String fileName, long fileSize)
1086            throws InterruptedException, IOException {
1087        URL url = UPLOAD_SESSION_URL_TEMPLATE.build(this.getAPI().getBaseUploadURL());
1088        return new LargeFileUpload().
1089                upload(this.getAPI(), this.getID(), inputStream, url, fileName, fileSize);
1090    }
1091
1092    /**
1093     * Creates a new file using specified number of parallel http connections.
1094     *
1095     * @param inputStream          the stream instance that contains the data.
1096     * @param fileName             the name of the file to be created.
1097     * @param fileSize             the size of the file that will be uploaded.
1098     * @param nParallelConnections number of parallel http connections to use
1099     * @param timeOut              time to wait before killing the job
1100     * @param unit                 time unit for the time wait value
1101     * @return the created file instance.
1102     * @throws InterruptedException when a thread execution is interrupted.
1103     * @throws IOException          when reading a stream throws exception.
1104     */
1105    public BoxFile.Info uploadLargeFile(InputStream inputStream, String fileName, long fileSize,
1106                                        int nParallelConnections, long timeOut, TimeUnit unit)
1107            throws InterruptedException, IOException {
1108        URL url = UPLOAD_SESSION_URL_TEMPLATE.build(this.getAPI().getBaseUploadURL());
1109        return new LargeFileUpload(nParallelConnections, timeOut, unit).
1110                upload(this.getAPI(), this.getID(), inputStream, url, fileName, fileSize);
1111    }
1112
1113    /**
1114     * Creates a new Metadata Cascade Policy on a folder.
1115     *
1116     * @param scope         the scope of the metadata cascade policy.
1117     * @param templateKey   the key of the template.
1118     * @return  information about the Metadata Cascade Policy.
1119     */
1120    public BoxMetadataCascadePolicy.Info addMetadataCascadePolicy(String scope, String templateKey) {
1121
1122        return BoxMetadataCascadePolicy.create(this.getAPI(), this.getID(), scope, templateKey);
1123    }
1124
1125    /**
1126     * Retrieves all Metadata Cascade Policies on a folder.
1127     *
1128     * @param fields            optional fields to retrieve for cascade policies.
1129     * @return  the Iterable of Box Metadata Cascade Policies in your enterprise.
1130     */
1131    public Iterable<BoxMetadataCascadePolicy.Info> getMetadataCascadePolicies(String... fields) {
1132        Iterable<BoxMetadataCascadePolicy.Info> cascadePoliciesInfo =
1133                BoxMetadataCascadePolicy.getAll(this.getAPI(), this.getID(), fields);
1134
1135        return cascadePoliciesInfo;
1136    }
1137
1138    /**
1139     * Retrieves all Metadata Cascade Policies on a folder.
1140     *
1141     * @param enterpriseID      the ID of the enterprise to retrieve cascade policies for.
1142     * @param limit             the number of entries of cascade policies to retrieve.
1143     * @param fields            optional fields to retrieve for cascade policies.
1144     * @return  the Iterable of Box Metadata Cascade Policies in your enterprise.
1145     */
1146    public Iterable<BoxMetadataCascadePolicy.Info> getMetadataCascadePolicies(String enterpriseID,
1147                                                                      int limit, String... fields) {
1148        Iterable<BoxMetadataCascadePolicy.Info> cascadePoliciesInfo =
1149                BoxMetadataCascadePolicy.getAll(this.getAPI(), this.getID(), enterpriseID, limit, fields);
1150
1151        return cascadePoliciesInfo;
1152    }
1153
1154    /**
1155     * Contains information about a BoxFolder.
1156     */
1157    public class Info extends BoxItem.Info {
1158        private BoxUploadEmail uploadEmail;
1159        private boolean hasCollaborations;
1160        private SyncState syncState;
1161        private EnumSet<Permission> permissions;
1162        private boolean canNonOwnersInvite;
1163        private boolean isWatermarked;
1164        private boolean isCollaborationRestrictedToEnterprise;
1165        private Boolean isExternallyOwned;
1166        private Map<String, Map<String, Metadata>> metadataMap;
1167
1168        /**
1169         * Constructs an empty Info object.
1170         */
1171        public Info() {
1172            super();
1173        }
1174
1175        /**
1176         * Constructs an Info object by parsing information from a JSON string.
1177         *
1178         * @param json the JSON string to parse.
1179         */
1180        public Info(String json) {
1181            super(json);
1182        }
1183
1184        /**
1185         * Constructs an Info object using an already parsed JSON object.
1186         *
1187         * @param jsonObject the parsed JSON object.
1188         */
1189        public Info(JsonObject jsonObject) {
1190            super(jsonObject);
1191        }
1192
1193        /**
1194         * Gets the upload email for the folder.
1195         *
1196         * @return the upload email for the folder.
1197         */
1198        public BoxUploadEmail getUploadEmail() {
1199            return this.uploadEmail;
1200        }
1201
1202        /**
1203         * Sets the upload email for the folder.
1204         *
1205         * @param uploadEmail the upload email for the folder.
1206         */
1207        public void setUploadEmail(BoxUploadEmail uploadEmail) {
1208            if (this.uploadEmail == uploadEmail) {
1209                return;
1210            }
1211
1212            this.removeChildObject("folder_upload_email");
1213            this.uploadEmail = uploadEmail;
1214
1215            if (uploadEmail == null) {
1216                this.addPendingChange("folder_upload_email", (String) null);
1217            } else {
1218                this.addChildObject("folder_upload_email", uploadEmail);
1219            }
1220        }
1221
1222        /**
1223         * Gets whether or not the folder has any collaborations.
1224         *
1225         * @return true if the folder has collaborations; otherwise false.
1226         */
1227        public boolean getHasCollaborations() {
1228            return this.hasCollaborations;
1229        }
1230
1231        /**
1232         * Gets the sync state of the folder.
1233         *
1234         * @return the sync state of the folder.
1235         */
1236        public SyncState getSyncState() {
1237            return this.syncState;
1238        }
1239
1240        /**
1241         * Sets the sync state of the folder.
1242         *
1243         * @param syncState the sync state of the folder.
1244         */
1245        public void setSyncState(SyncState syncState) {
1246            this.syncState = syncState;
1247            this.addPendingChange("sync_state", syncState.toJSONValue());
1248        }
1249
1250        /**
1251         * Gets the permissions that the current user has on the folder.
1252         *
1253         * @return the permissions that the current user has on the folder.
1254         */
1255        public EnumSet<Permission> getPermissions() {
1256            return this.permissions;
1257        }
1258
1259        /**
1260         * Gets whether or not the non-owners can invite collaborators to the folder.
1261         *
1262         * @return [description]
1263         */
1264        public boolean getCanNonOwnersInvite() {
1265            return this.canNonOwnersInvite;
1266        }
1267
1268        /**
1269         * Sets whether or not non-owners can invite collaborators to the folder.
1270         *
1271         * @param canNonOwnersInvite indicates non-owners can invite collaborators to the folder.
1272         */
1273        public void setCanNonOwnersInvite(boolean canNonOwnersInvite) {
1274            this.canNonOwnersInvite = canNonOwnersInvite;
1275            this.addPendingChange("can_non_owners_invite", canNonOwnersInvite);
1276        }
1277
1278        /**
1279         * Gets whether future collaborations should be restricted to within the enterprise only.
1280         *
1281         * @return indicates whether collaboration is restricted to enterprise only.
1282         */
1283        public boolean getIsCollaborationRestrictedToEnterprise() {
1284            return this.isCollaborationRestrictedToEnterprise;
1285        }
1286
1287        /**
1288         * Sets whether future collaborations should be restricted to within the enterprise only.
1289         *
1290         * @param isRestricted indicates whether there is collaboration restriction within enterprise.
1291         */
1292        public void setIsCollaborationRestrictedToEnterprise(boolean isRestricted) {
1293            this.isCollaborationRestrictedToEnterprise = isRestricted;
1294            this.addPendingChange("is_collaboration_restricted_to_enterprise", isRestricted);
1295        }
1296
1297        /**
1298         * Gets flag indicating whether this file is Watermarked.
1299         *
1300         * @return whether the file is watermarked or not
1301         */
1302        public boolean getIsWatermarked() {
1303            return this.isWatermarked;
1304        }
1305
1306        /**
1307         * Gets the metadata on this folder associated with a specified scope and template.
1308         * Makes an attempt to get metadata that was retrieved using getInfo(String ...) method. If no result is found
1309         * then makes an API call to get metadata
1310         *
1311         * @param templateName the metadata template type name.
1312         * @param scope        the scope of the template (usually "global" or "enterprise").
1313         * @return the metadata returned from the server.
1314         */
1315        public Metadata getMetadata(String templateName, String scope) {
1316            try {
1317                return this.metadataMap.get(scope).get(templateName);
1318            } catch (NullPointerException e) {
1319                return null;
1320            }
1321        }
1322
1323        /**
1324         * Get the field is_externally_owned determining whether this folder is owned by a user outside of the
1325         * enterprise.
1326         * @return a boolean indicating whether this folder is owned by a user outside the enterprise.
1327         */
1328        public boolean getIsExternallyOwned() {
1329            return this.isExternallyOwned;
1330        }
1331
1332        @Override
1333        public BoxFolder getResource() {
1334            return BoxFolder.this;
1335        }
1336
1337        @Override
1338        protected void parseJSONMember(JsonObject.Member member) {
1339            super.parseJSONMember(member);
1340
1341            String memberName = member.getName();
1342            JsonValue value = member.getValue();
1343            if (memberName.equals("folder_upload_email")) {
1344                if (this.uploadEmail == null) {
1345                    this.uploadEmail = new BoxUploadEmail(value.asObject());
1346                } else {
1347                    this.uploadEmail.update(value.asObject());
1348                }
1349
1350            } else if (memberName.equals("has_collaborations")) {
1351                this.hasCollaborations = value.asBoolean();
1352
1353            } else if (memberName.equals("sync_state")) {
1354                this.syncState = SyncState.fromJSONValue(value.asString());
1355
1356            } else if (memberName.equals("permissions")) {
1357                this.permissions = this.parsePermissions(value.asObject());
1358
1359            } else if (memberName.equals("can_non_owners_invite")) {
1360                this.canNonOwnersInvite = value.asBoolean();
1361            } else if (memberName.equals("is_collaboration_restricted_to_enterprise")) {
1362                this.isCollaborationRestrictedToEnterprise = value.asBoolean();
1363            } else if (memberName.equals("is_externally_owned")) {
1364                this.isExternallyOwned = value.asBoolean();
1365            } else if (memberName.equals("watermark_info")) {
1366                JsonObject jsonObject = value.asObject();
1367                this.isWatermarked = jsonObject.get("is_watermarked").asBoolean();
1368            } else if (memberName.equals("metadata")) {
1369                JsonObject jsonObject = value.asObject();
1370                this.metadataMap = Parsers.parseAndPopulateMetadataMap(jsonObject);
1371            }
1372        }
1373
1374        private EnumSet<Permission> parsePermissions(JsonObject jsonObject) {
1375            EnumSet<Permission> permissions = EnumSet.noneOf(Permission.class);
1376            for (JsonObject.Member member : jsonObject) {
1377                JsonValue value = member.getValue();
1378                if (value.isNull() || !value.asBoolean()) {
1379                    continue;
1380                }
1381
1382                String memberName = member.getName();
1383                if (memberName.equals("can_download")) {
1384                    permissions.add(Permission.CAN_DOWNLOAD);
1385                } else if (memberName.equals("can_upload")) {
1386                    permissions.add(Permission.CAN_UPLOAD);
1387                } else if (memberName.equals("can_rename")) {
1388                    permissions.add(Permission.CAN_RENAME);
1389                } else if (memberName.equals("can_delete")) {
1390                    permissions.add(Permission.CAN_DELETE);
1391                } else if (memberName.equals("can_share")) {
1392                    permissions.add(Permission.CAN_SHARE);
1393                } else if (memberName.equals("can_invite_collaborator")) {
1394                    permissions.add(Permission.CAN_INVITE_COLLABORATOR);
1395                } else if (memberName.equals("can_set_share_access")) {
1396                    permissions.add(Permission.CAN_SET_SHARE_ACCESS);
1397                }
1398            }
1399
1400            return permissions;
1401        }
1402    }
1403
1404    /**
1405     * Enumerates the possible sync states that a folder can have.
1406     */
1407    public enum SyncState {
1408        /**
1409         * The folder is synced.
1410         */
1411        SYNCED("synced"),
1412
1413        /**
1414         * The folder is not synced.
1415         */
1416        NOT_SYNCED("not_synced"),
1417
1418        /**
1419         * The folder is partially synced.
1420         */
1421        PARTIALLY_SYNCED("partially_synced");
1422
1423        private final String jsonValue;
1424
1425        private SyncState(String jsonValue) {
1426            this.jsonValue = jsonValue;
1427        }
1428
1429        static SyncState fromJSONValue(String jsonValue) {
1430            return SyncState.valueOf(jsonValue.toUpperCase());
1431        }
1432
1433        String toJSONValue() {
1434            return this.jsonValue;
1435        }
1436    }
1437
1438    /**
1439     * Enumerates the possible permissions that a user can have on a folder.
1440     */
1441    public enum Permission {
1442        /**
1443         * The user can download the folder.
1444         */
1445        CAN_DOWNLOAD("can_download"),
1446
1447        /**
1448         * The user can upload to the folder.
1449         */
1450        CAN_UPLOAD("can_upload"),
1451
1452        /**
1453         * The user can rename the folder.
1454         */
1455        CAN_RENAME("can_rename"),
1456
1457        /**
1458         * The user can delete the folder.
1459         */
1460        CAN_DELETE("can_delete"),
1461
1462        /**
1463         * The user can share the folder.
1464         */
1465        CAN_SHARE("can_share"),
1466
1467        /**
1468         * The user can invite collaborators to the folder.
1469         */
1470        CAN_INVITE_COLLABORATOR("can_invite_collaborator"),
1471
1472        /**
1473         * The user can set the access level for shared links to the folder.
1474         */
1475        CAN_SET_SHARE_ACCESS("can_set_share_access");
1476
1477        private final String jsonValue;
1478
1479        private Permission(String jsonValue) {
1480            this.jsonValue = jsonValue;
1481        }
1482
1483        static Permission fromJSONValue(String jsonValue) {
1484            return Permission.valueOf(jsonValue.toUpperCase());
1485        }
1486
1487        String toJSONValue() {
1488            return this.jsonValue;
1489        }
1490    }
1491}