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