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