001package com.box.sdk;
002
003import java.io.InputStream;
004import java.net.URL;
005import java.util.ArrayList;
006import java.util.Collection;
007import java.util.Date;
008import java.util.EnumSet;
009import java.util.Iterator;
010
011import com.eclipsesource.json.JsonArray;
012import com.eclipsesource.json.JsonObject;
013import com.eclipsesource.json.JsonValue;
014
015/**
016 * Represents a folder on Box. This class can be used to iterate through a folder's contents, collaborate a folder with
017 * another user or group, and perform other common folder operations (move, copy, delete, etc.).
018 *
019 * <p>Unless otherwise noted, the methods in this class can throw an unchecked {@link BoxAPIException} (unchecked
020 * meaning that the compiler won't force you to handle it) if an error occurs. If you wish to implement custom error
021 * handling for errors related to the Box REST API, you should capture this exception explicitly.</p>
022 */
023@BoxResourceType("folder")
024public class BoxFolder extends BoxItem implements Iterable<BoxItem.Info> {
025    /**
026     * An array of all possible folder fields that can be requested when calling {@link #getInfo()}.
027     */
028    public static final String[] ALL_FIELDS = {"type", "id", "sequence_id", "etag", "name", "created_at", "modified_at",
029        "description", "size", "path_collection", "created_by", "modified_by", "trashed_at", "purged_at",
030        "content_created_at", "content_modified_at", "owned_by", "shared_link", "folder_upload_email", "parent",
031        "item_status", "item_collection", "sync_state", "has_collaborations", "permissions", "tags",
032        "can_non_owners_invite", "collections", "watermark_info"};
033
034    private static final URLTemplate CREATE_FOLDER_URL = new URLTemplate("folders");
035    private static final URLTemplate CREATE_WEB_LINK_URL = new URLTemplate("web_links");
036    private static final URLTemplate COPY_FOLDER_URL = new URLTemplate("folders/%s/copy");
037    private static final URLTemplate DELETE_FOLDER_URL = new URLTemplate("folders/%s?recursive=%b");
038    private static final URLTemplate FOLDER_INFO_URL_TEMPLATE = new URLTemplate("folders/%s");
039    private static final URLTemplate UPLOAD_FILE_URL = new URLTemplate("files/content");
040    private static final URLTemplate ADD_COLLABORATION_URL = new URLTemplate("collaborations");
041    private static final URLTemplate GET_COLLABORATIONS_URL = new URLTemplate("folders/%s/collaborations");
042    private static final URLTemplate GET_ITEMS_URL = new URLTemplate("folders/%s/items/");
043    private static final URLTemplate SEARCH_URL_TEMPLATE = new URLTemplate("search");
044    private static final URLTemplate METADATA_URL_TEMPLATE = new URLTemplate("folders/%s/metadata/%s/%s");
045
046    /**
047     * Constructs a BoxFolder for a folder with a given ID.
048     * @param  api the API connection to be used by the folder.
049     * @param  id  the ID of the folder.
050     */
051    public BoxFolder(BoxAPIConnection api, String id) {
052        super(api, id);
053    }
054
055    /**
056     * {@inheritDoc}
057     */
058    @Override
059    protected URL getItemURL() {
060        return FOLDER_INFO_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
061    }
062
063    /**
064     * Gets the current user's root folder.
065     * @param  api the API connection to be used by the folder.
066     * @return     the user's root folder.
067     */
068    public static BoxFolder getRootFolder(BoxAPIConnection api) {
069        return new BoxFolder(api, "0");
070    }
071
072    /**
073     * Adds a collaborator to this folder.
074     * @param  collaborator the collaborator to add.
075     * @param  role         the role of the collaborator.
076     * @return              info about the new collaboration.
077     */
078    public BoxCollaboration.Info collaborate(BoxCollaborator collaborator, BoxCollaboration.Role role) {
079        JsonObject accessibleByField = new JsonObject();
080        accessibleByField.add("id", collaborator.getID());
081
082        if (collaborator instanceof BoxUser) {
083            accessibleByField.add("type", "user");
084        } else if (collaborator instanceof BoxGroup) {
085            accessibleByField.add("type", "group");
086        } else {
087            throw new IllegalArgumentException("The given collaborator is of an unknown type.");
088        }
089
090        return this.collaborate(accessibleByField, role);
091    }
092
093    /**
094     * Adds a collaborator to this folder. An email will be sent to the collaborator if they don't already have a Box
095     * account.
096     * @param  email the email address of the collaborator to add.
097     * @param  role  the role of the collaborator.
098     * @return       info about the new collaboration.
099     */
100    public BoxCollaboration.Info collaborate(String email, BoxCollaboration.Role role) {
101        JsonObject accessibleByField = new JsonObject();
102        accessibleByField.add("login", email);
103        accessibleByField.add("type", "user");
104
105        return this.collaborate(accessibleByField, role);
106    }
107
108    private BoxCollaboration.Info collaborate(JsonObject accessibleByField, BoxCollaboration.Role role) {
109        BoxAPIConnection api = this.getAPI();
110        URL url = ADD_COLLABORATION_URL.build(api.getBaseURL());
111
112        JsonObject itemField = new JsonObject();
113        itemField.add("id", this.getID());
114        itemField.add("type", "folder");
115
116        JsonObject requestJSON = new JsonObject();
117        requestJSON.add("item", itemField);
118        requestJSON.add("accessible_by", accessibleByField);
119        requestJSON.add("role", role.toJSONString());
120
121        BoxJSONRequest request = new BoxJSONRequest(api, url, "POST");
122        request.setBody(requestJSON.toString());
123        BoxJSONResponse response = (BoxJSONResponse) request.send();
124        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
125
126        BoxCollaboration newCollaboration = new BoxCollaboration(api, responseJSON.get("id").asString());
127        BoxCollaboration.Info info = newCollaboration.new Info(responseJSON);
128        return info;
129    }
130
131    @Override
132    public BoxSharedLink createSharedLink(BoxSharedLink.Access access, Date unshareDate,
133        BoxSharedLink.Permissions permissions) {
134
135        BoxSharedLink sharedLink = new BoxSharedLink(access, unshareDate, permissions);
136        Info info = new Info();
137        info.setSharedLink(sharedLink);
138
139        this.updateInfo(info);
140        return info.getSharedLink();
141    }
142
143    /**
144     * Gets information about all of the collaborations for this folder.
145     * @return a collection of information about the collaborations for this folder.
146     */
147    public Collection<BoxCollaboration.Info> getCollaborations() {
148        BoxAPIConnection api = this.getAPI();
149        URL url = GET_COLLABORATIONS_URL.build(api.getBaseURL(), this.getID());
150
151        BoxAPIRequest request = new BoxAPIRequest(api, url, "GET");
152        BoxJSONResponse response = (BoxJSONResponse) request.send();
153        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
154
155        int entriesCount = responseJSON.get("total_count").asInt();
156        Collection<BoxCollaboration.Info> collaborations = new ArrayList<BoxCollaboration.Info>(entriesCount);
157        JsonArray entries = responseJSON.get("entries").asArray();
158        for (JsonValue entry : entries) {
159            JsonObject entryObject = entry.asObject();
160            BoxCollaboration collaboration = new BoxCollaboration(api, entryObject.get("id").asString());
161            BoxCollaboration.Info info = collaboration.new Info(entryObject);
162            collaborations.add(info);
163        }
164
165        return collaborations;
166    }
167
168    @Override
169    public BoxFolder.Info getInfo() {
170        URL url = FOLDER_INFO_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
171        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET");
172        BoxJSONResponse response = (BoxJSONResponse) request.send();
173        return new Info(response.getJSON());
174    }
175
176    @Override
177    public BoxFolder.Info getInfo(String... fields) {
178        String queryString = new QueryStringBuilder().appendParam("fields", fields).toString();
179        URL url = FOLDER_INFO_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), queryString, this.getID());
180
181        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET");
182        BoxJSONResponse response = (BoxJSONResponse) request.send();
183        return new Info(response.getJSON());
184    }
185
186    /**
187     * Updates the information about this folder with any info fields that have been modified locally.
188     * @param info the updated info.
189     */
190    public void updateInfo(BoxFolder.Info info) {
191        URL url = FOLDER_INFO_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
192        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT");
193        request.setBody(info.getPendingChanges());
194        BoxJSONResponse response = (BoxJSONResponse) request.send();
195        JsonObject jsonObject = JsonObject.readFrom(response.getJSON());
196        info.update(jsonObject);
197    }
198
199    @Override
200    public BoxFolder.Info copy(BoxFolder destination) {
201        return this.copy(destination, null);
202    }
203
204    @Override
205    public BoxFolder.Info copy(BoxFolder destination, String newName) {
206        URL url = COPY_FOLDER_URL.build(this.getAPI().getBaseURL(), this.getID());
207        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "POST");
208
209        JsonObject parent = new JsonObject();
210        parent.add("id", destination.getID());
211
212        JsonObject copyInfo = new JsonObject();
213        copyInfo.add("parent", parent);
214        if (newName != null) {
215            copyInfo.add("name", newName);
216        }
217
218        request.setBody(copyInfo.toString());
219        BoxJSONResponse response = (BoxJSONResponse) request.send();
220        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
221        BoxFolder copiedFolder = new BoxFolder(this.getAPI(), responseJSON.get("id").asString());
222        return copiedFolder.new Info(responseJSON);
223    }
224
225    /**
226     * Creates a new child folder inside this folder.
227     * @param  name the new folder's name.
228     * @return      the created folder's info.
229     */
230    public BoxFolder.Info createFolder(String name) {
231        JsonObject parent = new JsonObject();
232        parent.add("id", this.getID());
233
234        JsonObject newFolder = new JsonObject();
235        newFolder.add("name", name);
236        newFolder.add("parent", parent);
237
238        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), CREATE_FOLDER_URL.build(this.getAPI().getBaseURL()),
239            "POST");
240        request.setBody(newFolder.toString());
241        BoxJSONResponse response = (BoxJSONResponse) request.send();
242        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
243
244        BoxFolder createdFolder = new BoxFolder(this.getAPI(), responseJSON.get("id").asString());
245        return createdFolder.new Info(responseJSON);
246    }
247
248    /**
249     * Deletes this folder, optionally recursively deleting all of its contents.
250     * @param recursive true to recursively delete this folder's contents; otherwise false.
251     */
252    public void delete(boolean recursive) {
253        URL url = DELETE_FOLDER_URL.build(this.getAPI().getBaseURL(), this.getID(), recursive);
254        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "DELETE");
255        BoxAPIResponse response = request.send();
256        response.disconnect();
257    }
258
259    @Override
260    public BoxItem.Info move(BoxFolder destination) {
261        return this.move(destination, null);
262    }
263
264    @Override
265    public BoxItem.Info move(BoxFolder destination, String newName) {
266        URL url = FOLDER_INFO_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
267        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT");
268
269        JsonObject parent = new JsonObject();
270        parent.add("id", destination.getID());
271
272        JsonObject updateInfo = new JsonObject();
273        updateInfo.add("parent", parent);
274        if (newName != null) {
275            updateInfo.add("name", newName);
276        }
277
278        request.setBody(updateInfo.toString());
279        BoxJSONResponse response = (BoxJSONResponse) request.send();
280        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
281        BoxFolder movedFolder = new BoxFolder(this.getAPI(), responseJSON.get("id").asString());
282        return movedFolder.new Info(responseJSON);
283    }
284
285    /**
286     * Renames this folder.
287     * @param newName the new name of the folder.
288     */
289    public void rename(String newName) {
290        URL url = FOLDER_INFO_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
291        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT");
292
293        JsonObject updateInfo = new JsonObject();
294        updateInfo.add("name", newName);
295
296        request.setBody(updateInfo.toString());
297        BoxAPIResponse response = request.send();
298        response.disconnect();
299    }
300
301    /**
302     * Checks if the file can be successfully uploaded by using the preflight check.
303     * @param  name        the name to give the uploaded file.
304     * @param  fileSize    the size of the file used for account capacity calculations.
305     */
306    public void canUpload(String name, long fileSize) {
307        URL url = UPLOAD_FILE_URL.build(this.getAPI().getBaseURL());
308        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "OPTIONS");
309
310        JsonObject parent = new JsonObject();
311        parent.add("id", this.getID());
312
313        JsonObject preflightInfo = new JsonObject();
314        preflightInfo.add("parent", parent);
315        preflightInfo.add("name", name);
316
317        preflightInfo.add("size", fileSize);
318
319        request.setBody(preflightInfo.toString());
320        BoxAPIResponse response = request.send();
321        response.disconnect();
322    }
323
324    /**
325     * Uploads a new file to this folder.
326     * @param  fileContent a stream containing the contents of the file to upload.
327     * @param  name        the name to give the uploaded file.
328     * @return             the uploaded file's info.
329     */
330    public BoxFile.Info uploadFile(InputStream fileContent, String name) {
331        FileUploadParams uploadInfo = new FileUploadParams()
332            .setContent(fileContent)
333            .setName(name);
334        return this.uploadFile(uploadInfo);
335    }
336
337    /**
338     * Uploads a new file to this folder while reporting the progress to a ProgressListener.
339     * @param  fileContent a stream containing the contents of the file to upload.
340     * @param  name        the name to give the uploaded file.
341     * @param  fileSize    the size of the file used for determining the progress of the upload.
342     * @param  listener    a listener for monitoring the upload's progress.
343     * @return             the uploaded file's info.
344     */
345    public BoxFile.Info uploadFile(InputStream fileContent, String name, long fileSize, ProgressListener listener) {
346        FileUploadParams uploadInfo = new FileUploadParams()
347            .setContent(fileContent)
348            .setName(name)
349            .setSize(fileSize)
350            .setProgressListener(listener);
351        return this.uploadFile(uploadInfo);
352    }
353
354    /**
355     * Uploads a new file to this folder with custom upload parameters.
356     * @param  uploadParams the custom upload parameters.
357     * @return              the uploaded file's info.
358     */
359    public BoxFile.Info uploadFile(FileUploadParams uploadParams) {
360        URL uploadURL = UPLOAD_FILE_URL.build(this.getAPI().getBaseUploadURL());
361        BoxMultipartRequest request = new BoxMultipartRequest(getAPI(), uploadURL);
362
363        JsonObject fieldJSON = new JsonObject();
364        JsonObject parentIdJSON = new JsonObject();
365        parentIdJSON.add("id", getID());
366        fieldJSON.add("name", uploadParams.getName());
367        fieldJSON.add("parent", parentIdJSON);
368
369        if (uploadParams.getCreated() != null) {
370            fieldJSON.add("content_created_at", BoxDateFormat.format(uploadParams.getCreated()));
371        }
372
373        if (uploadParams.getModified() != null) {
374            fieldJSON.add("content_modified_at", BoxDateFormat.format(uploadParams.getModified()));
375        }
376
377        request.putField("attributes", fieldJSON.toString());
378
379        if (uploadParams.getSize() > 0) {
380            request.setFile(uploadParams.getContent(), uploadParams.getName(), uploadParams.getSize());
381        } else {
382            request.setFile(uploadParams.getContent(), uploadParams.getName());
383        }
384
385        BoxJSONResponse response;
386        if (uploadParams.getProgressListener() == null) {
387            response = (BoxJSONResponse) request.send();
388        } else {
389            response = (BoxJSONResponse) request.send(uploadParams.getProgressListener());
390        }
391        JsonObject collection = JsonObject.readFrom(response.getJSON());
392        JsonArray entries = collection.get("entries").asArray();
393        JsonObject fileInfoJSON = entries.get(0).asObject();
394        String uploadedFileID = fileInfoJSON.get("id").asString();
395
396        BoxFile uploadedFile = new BoxFile(getAPI(), uploadedFileID);
397        return uploadedFile.new Info(fileInfoJSON);
398    }
399
400    /**
401     * Uploads a new weblink to this folder.
402     * @param  linkURL     the URL the weblink points to.
403     * @return             the uploaded weblink's info.
404     */
405    public BoxWebLink.Info createWebLink(URL linkURL) {
406        return this.createWebLink(null, linkURL, null);
407    }
408
409    /**
410     * Uploads a new weblink to this folder.
411     * @param  name        the filename for the weblink.
412     * @param  linkURL     the URL the weblink points to.
413     * @return             the uploaded weblink's info.
414     */
415    public BoxWebLink.Info createWebLink(String name, URL linkURL) {
416        return this.createWebLink(name, linkURL, null);
417    }
418
419    /**
420     * Uploads a new weblink to this folder.
421     * @param  linkURL     the URL the weblink points to.
422     * @param  description the weblink's description.
423     * @return             the uploaded weblink's info.
424     */
425    public BoxWebLink.Info createWebLink(URL linkURL, String description) {
426        return this.createWebLink(null, linkURL, description);
427    }
428
429    /**
430     * Uploads a new weblink to this folder.
431     * @param  name        the filename for the weblink.
432     * @param  linkURL     the URL the weblink points to.
433     * @param  description the weblink's description.
434     * @return             the uploaded weblink's info.
435     */
436    public BoxWebLink.Info createWebLink(String name, URL linkURL, String description) {
437        JsonObject parent = new JsonObject();
438        parent.add("id", this.getID());
439
440        JsonObject newWebLink = new JsonObject();
441        newWebLink.add("name", name);
442        newWebLink.add("parent", parent);
443        newWebLink.add("url", linkURL.toString());
444
445        if (description != null) {
446            newWebLink.add("description", description);
447        }
448
449        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(),
450            CREATE_WEB_LINK_URL.build(this.getAPI().getBaseURL()), "POST");
451        request.setBody(newWebLink.toString());
452        BoxJSONResponse response = (BoxJSONResponse) request.send();
453        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
454
455        BoxWebLink createdWebLink = new BoxWebLink(this.getAPI(), responseJSON.get("id").asString());
456        return createdWebLink.new Info(responseJSON);
457    }
458
459    /**
460     * Returns an iterable containing the items in this folder. Iterating over the iterable returned by this method is
461     * equivalent to iterating over this BoxFolder directly.
462     * @return an iterable containing the items in this folder.
463     */
464    public Iterable<BoxItem.Info> getChildren() {
465        return this;
466    }
467
468    /**
469     * Returns an iterable containing the items in this folder and specifies which child fields to retrieve from the
470     * API.
471     * @param  fields the fields to retrieve.
472     * @return        an iterable containing the items in this folder.
473     */
474    public Iterable<BoxItem.Info> getChildren(final String... fields) {
475        return new Iterable<BoxItem.Info>() {
476            @Override
477            public Iterator<BoxItem.Info> iterator() {
478                String queryString = new QueryStringBuilder().appendParam("fields", fields).toString();
479                URL url = GET_ITEMS_URL.buildWithQuery(getAPI().getBaseURL(), queryString, getID());
480                return new BoxItemIterator(getAPI(), url);
481            }
482        };
483    }
484
485    /**
486     * Retrieves a specific range of child items in this folder.
487     * @param  offset the index of the first child item to retrieve.
488     * @param  limit  the maximum number of children to retrieve after the offset.
489     * @param  fields the fields to retrieve.
490     * @return        a partial collection containing the specified range of child items.
491     */
492    public PartialCollection<BoxItem.Info> getChildrenRange(long offset, long limit, String... fields) {
493        QueryStringBuilder builder = new QueryStringBuilder()
494            .appendParam("limit", limit)
495            .appendParam("offset", offset);
496
497        if (fields.length > 0) {
498            builder.appendParam("fields", fields).toString();
499        }
500
501        URL url = GET_ITEMS_URL.buildWithQuery(getAPI().getBaseURL(), builder.toString(), getID());
502        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET");
503        BoxJSONResponse response = (BoxJSONResponse) request.send();
504        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
505
506        String totalCountString = responseJSON.get("total_count").toString();
507        long fullSize = Double.valueOf(totalCountString).longValue();
508        PartialCollection<BoxItem.Info> children = new PartialCollection<BoxItem.Info>(offset, limit, fullSize);
509        JsonArray jsonArray = responseJSON.get("entries").asArray();
510        for (JsonValue value : jsonArray) {
511            JsonObject jsonObject = value.asObject();
512            BoxItem.Info parsedItemInfo = (BoxItem.Info) BoxResource.parseInfo(this.getAPI(), jsonObject);
513            if (parsedItemInfo != null) {
514                children.add(parsedItemInfo);
515            }
516        }
517        return children;
518    }
519
520    /**
521     * Returns an iterator over the items in this folder.
522     * @return an iterator over the items in this folder.
523     */
524    @Override
525    public Iterator<BoxItem.Info> iterator() {
526        URL url = GET_ITEMS_URL.build(this.getAPI().getBaseURL(), BoxFolder.this.getID());
527        return new BoxItemIterator(BoxFolder.this.getAPI(), url);
528    }
529
530    /**
531     * Adds new {@link BoxWebHook} to this {@link BoxFolder}.
532     *
533     * @param address
534     *            {@link BoxWebHook.Info#getAddress()}
535     * @param triggers
536     *            {@link BoxWebHook.Info#getTriggers()}
537     * @return created {@link BoxWebHook.Info}
538     */
539    public BoxWebHook.Info addWebHook(URL address, BoxWebHook.Trigger... triggers) {
540        return BoxWebHook.create(this, address, triggers);
541    }
542
543    /**
544
545     * Used to retrieve the watermark for the folder.
546     * If the folder does not have a watermark applied to it, a 404 Not Found will be returned by API.
547     * @param fields the fields to retrieve.
548     * @return the watermark associated with the folder.
549     */
550    public BoxWatermark getWatermark(String... fields) {
551        return this.getWatermark(FOLDER_INFO_URL_TEMPLATE, fields);
552    }
553
554    /**
555     * Used to apply or update the watermark for the folder.
556     * @return the watermark associated with the folder.
557     */
558    public BoxWatermark applyWatermark() {
559        return this.applyWatermark(FOLDER_INFO_URL_TEMPLATE, BoxWatermark.WATERMARK_DEFAULT_IMPRINT);
560    }
561
562    /**
563     * Removes a watermark from the folder.
564     * If the folder did not have a watermark applied to it, a 404 Not Found will be returned by API.
565     */
566    public void removeWatermark() {
567        this.removeWatermark(FOLDER_INFO_URL_TEMPLATE);
568    }
569
570    /**
571     * Used to retrieve all metadata associated with the folder.
572     *
573     * @param fields the optional fields to retrieve.
574     * @return An iterable of metadata instances associated with the folder
575     */
576    public Iterable<Metadata> getAllMetadata(String... fields) {
577        return Metadata.getAllMetadata(this, fields);
578    }
579
580    /**
581     * This method is deprecated, please use the {@link BoxSearch} class instead.
582     * Searches this folder and all descendant folders using a given queryPlease use BoxSearch Instead.
583     * @param  query the search query.
584     * @return an Iterable containing the search results.
585     */
586    @Deprecated
587    public Iterable<BoxItem.Info> search(final String query) {
588        return new Iterable<BoxItem.Info>() {
589            @Override
590            public Iterator<BoxItem.Info> iterator() {
591                QueryStringBuilder builder = new QueryStringBuilder();
592                builder.appendParam("query", query);
593                builder.appendParam("ancestor_folder_ids", getID());
594
595                URL url = SEARCH_URL_TEMPLATE.buildWithQuery(getAPI().getBaseURL(), builder.toString());
596                return new BoxItemIterator(getAPI(), url);
597            }
598        };
599    }
600
601    @Override
602    public BoxFolder.Info setCollections(BoxCollection... collections) {
603        JsonArray jsonArray = new JsonArray();
604        for (BoxCollection collection : collections) {
605            JsonObject collectionJSON = new JsonObject();
606            collectionJSON.add("id", collection.getID());
607            jsonArray.add(collectionJSON);
608        }
609        JsonObject infoJSON = new JsonObject();
610        infoJSON.add("collections", jsonArray);
611
612        String queryString = new QueryStringBuilder().appendParam("fields", ALL_FIELDS).toString();
613        URL url = FOLDER_INFO_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), queryString, this.getID());
614        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT");
615        request.setBody(infoJSON.toString());
616        BoxJSONResponse response = (BoxJSONResponse) request.send();
617        JsonObject jsonObject = JsonObject.readFrom(response.getJSON());
618        return new Info(jsonObject);
619    }
620
621    /**
622     * Creates global property metadata on this folder.
623     * @param   metadata    the new metadata values.
624     * @return              the metadata returned from the server.
625     */
626    public Metadata createMetadata(Metadata metadata) {
627        return this.createMetadata(Metadata.DEFAULT_METADATA_TYPE, metadata);
628    }
629
630    /**
631     * Creates metadata on this folder using a specified template.
632     * @param   templateName    the name of the metadata template.
633     * @param   metadata        the new metadata values.
634     * @return                  the metadata returned from the server.
635     */
636    public Metadata createMetadata(String templateName, Metadata metadata) {
637        String scope = Metadata.scopeBasedOnType(templateName);
638        return this.createMetadata(templateName, scope, metadata);
639    }
640
641    /**
642     * Creates metadata on this folder using a specified scope and template.
643     * @param   templateName    the name of the metadata template.
644     * @param   scope           the scope of the template (usually "global" or "enterprise").
645     * @param   metadata        the new metadata values.
646     * @return                  the metadata returned from the server.
647     */
648    public Metadata createMetadata(String templateName, String scope, Metadata metadata) {
649        URL url = METADATA_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), scope, templateName);
650        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "POST");
651        request.addHeader("Content-Type", "application/json");
652        request.setBody(metadata.toString());
653        BoxJSONResponse response = (BoxJSONResponse) request.send();
654        return new Metadata(JsonObject.readFrom(response.getJSON()));
655    }
656
657    /**
658     * Gets the global properties metadata on this folder.
659     * @return the metadata returned from the server.
660     */
661    public Metadata getMetadata() {
662        return this.getMetadata(Metadata.DEFAULT_METADATA_TYPE);
663    }
664
665    /**
666     * Gets the metadata on this folder associated with a specified template.
667     * @param   templateName    the metadata template type name.
668     * @return                  the metadata returned from the server.
669     */
670    public Metadata getMetadata(String templateName) {
671        String scope = Metadata.scopeBasedOnType(templateName);
672        return this.getMetadata(templateName, scope);
673    }
674
675    /**
676     * Gets the metadata on this folder associated with a specified scope and template.
677     * @param   templateName    the metadata template type name.
678     * @param   scope           the scope of the template (usually "global" or "enterprise").
679     * @return                  the metadata returned from the server.
680     */
681    public Metadata getMetadata(String templateName, String scope) {
682        URL url = METADATA_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), scope, templateName);
683        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET");
684        BoxJSONResponse response = (BoxJSONResponse) request.send();
685        return new Metadata(JsonObject.readFrom(response.getJSON()));
686    }
687
688    /**
689     * Updates the global properties metadata on this folder.
690     * @param   metadata    the new metadata values.
691     * @return              the metadata returned from the server.
692     */
693    public Metadata updateMetadata(Metadata metadata) {
694        URL url = METADATA_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), metadata.getScope(),
695            metadata.getTemplateName());
696        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "PUT");
697        request.addHeader("Content-Type", "application/json-patch+json");
698        request.setBody(metadata.getPatch());
699        BoxJSONResponse response = (BoxJSONResponse) request.send();
700        return new Metadata(JsonObject.readFrom(response.getJSON()));
701    }
702
703    /**
704     * Deletes the global properties metadata on this folder.
705     */
706    public void deleteMetadata() {
707        this.deleteMetadata(Metadata.DEFAULT_METADATA_TYPE);
708    }
709
710    /**
711     * Deletes the metadata on this folder associated with a specified template.
712     * @param templateName the metadata template type name.
713     */
714    public void deleteMetadata(String templateName) {
715        String scope = Metadata.scopeBasedOnType(templateName);
716        this.deleteMetadata(templateName, scope);
717    }
718
719    /**
720     * Deletes the metadata on this folder associated with a specified scope and template.
721     * @param   templateName    the metadata template type name.
722     * @param   scope           the scope of the template (usually "global" or "enterprise").
723     */
724    public void deleteMetadata(String templateName, String scope) {
725        URL url = METADATA_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), scope, templateName);
726        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "DELETE");
727        BoxAPIResponse response = request.send();
728        response.disconnect();
729    }
730
731    /**
732     * Contains information about a BoxFolder.
733     */
734    public class Info extends BoxItem.Info {
735        private BoxUploadEmail uploadEmail;
736        private boolean hasCollaborations;
737        private SyncState syncState;
738        private EnumSet<Permission> permissions;
739        private boolean canNonOwnersInvite;
740        private boolean isWatermarked;
741
742        /**
743         * Constructs an empty Info object.
744         */
745        public Info() {
746            super();
747        }
748
749        /**
750         * Constructs an Info object by parsing information from a JSON string.
751         * @param  json the JSON string to parse.
752         */
753        public Info(String json) {
754            super(json);
755        }
756
757        /**
758         * Constructs an Info object using an already parsed JSON object.
759         * @param  jsonObject the parsed JSON object.
760         */
761        Info(JsonObject jsonObject) {
762            super(jsonObject);
763        }
764
765        /**
766         * Gets the upload email for the folder.
767         * @return the upload email for the folder.
768         */
769        public BoxUploadEmail getUploadEmail() {
770            return this.uploadEmail;
771        }
772
773        /**
774         * Sets the upload email for the folder.
775         * @param uploadEmail the upload email for the folder.
776         */
777        public void setUploadEmail(BoxUploadEmail uploadEmail) {
778            if (this.uploadEmail == uploadEmail) {
779                return;
780            }
781
782            this.removeChildObject("folder_upload_email");
783            this.uploadEmail = uploadEmail;
784
785            if (uploadEmail == null) {
786                this.addPendingChange("folder_upload_email", (String) null);
787            } else {
788                this.addChildObject("folder_upload_email", uploadEmail);
789            }
790        }
791
792        /**
793         * Gets whether or not the folder has any collaborations.
794         * @return true if the folder has collaborations; otherwise false.
795         */
796        public boolean getHasCollaborations() {
797            return this.hasCollaborations;
798        }
799
800        /**
801         * Gets the sync state of the folder.
802         * @return the sync state of the folder.
803         */
804        public SyncState getSyncState() {
805            return this.syncState;
806        }
807
808        /**
809         * Sets the sync state of the folder.
810         * @param syncState the sync state of the folder.
811         */
812        public void setSyncState(SyncState syncState) {
813            this.syncState = syncState;
814            this.addPendingChange("sync_state", syncState.toJSONValue());
815        }
816
817        /**
818         * Gets the permissions that the current user has on the folder.
819         * @return the permissions that the current user has on the folder.
820         */
821        public EnumSet<Permission> getPermissions() {
822            return this.permissions;
823        }
824
825        /**
826         * Gets whether or not the non-owners can invite collaborators to the folder.
827         * @return [description]
828         */
829        public boolean getCanNonOwnersInvite() {
830            return this.canNonOwnersInvite;
831        }
832
833        /**
834         * Gets flag indicating whether this file is Watermarked.
835         * @return whether the file is watermarked or not
836         */
837        public boolean getIsWatermarked() {
838            return this.isWatermarked;
839        }
840
841        @Override
842        public BoxFolder getResource() {
843            return BoxFolder.this;
844        }
845
846        @Override
847        protected void parseJSONMember(JsonObject.Member member) {
848            super.parseJSONMember(member);
849
850            String memberName = member.getName();
851            JsonValue value = member.getValue();
852            if (memberName.equals("folder_upload_email")) {
853                if (this.uploadEmail == null) {
854                    this.uploadEmail = new BoxUploadEmail(value.asObject());
855                } else {
856                    this.uploadEmail.update(value.asObject());
857                }
858
859            } else if (memberName.equals("has_collaborations")) {
860                this.hasCollaborations = value.asBoolean();
861
862            } else if (memberName.equals("sync_state")) {
863                this.syncState = SyncState.fromJSONValue(value.asString());
864
865            } else if (memberName.equals("permissions")) {
866                this.permissions = this.parsePermissions(value.asObject());
867
868            } else if (memberName.equals("can_non_owners_invite")) {
869                this.canNonOwnersInvite = value.asBoolean();
870            } else if (memberName.equals("watermark_info")) {
871                JsonObject jsonObject = value.asObject();
872                this.isWatermarked = jsonObject.get("is_watermarked").asBoolean();
873            }
874        }
875
876        private EnumSet<Permission> parsePermissions(JsonObject jsonObject) {
877            EnumSet<Permission> permissions = EnumSet.noneOf(Permission.class);
878            for (JsonObject.Member member : jsonObject) {
879                JsonValue value = member.getValue();
880                if (value.isNull() || !value.asBoolean()) {
881                    continue;
882                }
883
884                String memberName = member.getName();
885                if (memberName.equals("can_download")) {
886                    permissions.add(Permission.CAN_DOWNLOAD);
887                } else if (memberName.equals("can_upload")) {
888                    permissions.add(Permission.CAN_UPLOAD);
889                } else if (memberName.equals("can_rename")) {
890                    permissions.add(Permission.CAN_RENAME);
891                } else if (memberName.equals("can_delete")) {
892                    permissions.add(Permission.CAN_DELETE);
893                } else if (memberName.equals("can_share")) {
894                    permissions.add(Permission.CAN_SHARE);
895                } else if (memberName.equals("can_invite_collaborator")) {
896                    permissions.add(Permission.CAN_INVITE_COLLABORATOR);
897                } else if (memberName.equals("can_set_share_access")) {
898                    permissions.add(Permission.CAN_SET_SHARE_ACCESS);
899                }
900            }
901
902            return permissions;
903        }
904    }
905
906    /**
907     * Enumerates the possible sync states that a folder can have.
908     */
909    public enum SyncState {
910        /**
911         * The folder is synced.
912         */
913        SYNCED("synced"),
914
915        /**
916         * The folder is not synced.
917         */
918        NOT_SYNCED("not_synced"),
919
920        /**
921         * The folder is partially synced.
922         */
923        PARTIALLY_SYNCED("partially_synced");
924
925        private final String jsonValue;
926
927        private SyncState(String jsonValue) {
928            this.jsonValue = jsonValue;
929        }
930
931        static SyncState fromJSONValue(String jsonValue) {
932            return SyncState.valueOf(jsonValue.toUpperCase());
933        }
934
935        String toJSONValue() {
936            return this.jsonValue;
937        }
938    }
939
940    /**
941     * Enumerates the possible permissions that a user can have on a folder.
942     */
943    public enum Permission {
944        /**
945         * The user can download the folder.
946         */
947        CAN_DOWNLOAD("can_download"),
948
949        /**
950         * The user can upload to the folder.
951         */
952        CAN_UPLOAD("can_upload"),
953
954        /**
955         * The user can rename the folder.
956         */
957        CAN_RENAME("can_rename"),
958
959        /**
960         * The user can delete the folder.
961         */
962        CAN_DELETE("can_delete"),
963
964        /**
965         * The user can share the folder.
966         */
967        CAN_SHARE("can_share"),
968
969        /**
970         * The user can invite collaborators to the folder.
971         */
972        CAN_INVITE_COLLABORATOR("can_invite_collaborator"),
973
974        /**
975         * The user can set the access level for shared links to the folder.
976         */
977        CAN_SET_SHARE_ACCESS("can_set_share_access");
978
979        private final String jsonValue;
980
981        private Permission(String jsonValue) {
982            this.jsonValue = jsonValue;
983        }
984
985        static Permission fromJSONValue(String jsonValue) {
986            return Permission.valueOf(jsonValue.toUpperCase());
987        }
988
989        String toJSONValue() {
990            return this.jsonValue;
991        }
992    }
993}