001package com.box.sdk;
002
003import java.io.IOException;
004import java.io.InputStream;
005import java.io.OutputStream;
006import java.net.URL;
007import java.util.ArrayList;
008import java.util.Collection;
009import java.util.Date;
010import java.util.EnumSet;
011import java.util.List;
012
013import com.eclipsesource.json.JsonArray;
014import com.eclipsesource.json.JsonObject;
015import com.eclipsesource.json.JsonValue;
016
017/**
018 * Represents an individual file on Box. This class can be used to download a file's contents, upload new versions, and
019 * perform other common file operations (move, copy, delete, etc.).
020 *
021 * <p>Unless otherwise noted, the methods in this class can throw an unchecked {@link BoxAPIException} (unchecked
022 * meaning that the compiler won't force you to handle it) if an error occurs. If you wish to implement custom error
023 * handling for errors related to the Box REST API, you should capture this exception explicitly.</p>
024 */
025public class BoxFile extends BoxItem {
026    /**
027     * An array of all possible file fields that can be requested when calling {@link #getInfo()}.
028     */
029    public static final String[] ALL_FIELDS = {"type", "id", "sequence_id", "etag", "sha1", "name", "description",
030        "size", "path_collection", "created_at", "modified_at", "trashed_at", "purged_at", "content_created_at",
031        "content_modified_at", "created_by", "modified_by", "owned_by", "shared_link", "parent", "item_status",
032        "version_number", "comment_count", "permissions", "tags", "lock", "extension", "is_package", "file_version"};
033
034    private static final URLTemplate FILE_URL_TEMPLATE = new URLTemplate("files/%s");
035    private static final URLTemplate CONTENT_URL_TEMPLATE = new URLTemplate("files/%s/content");
036    private static final URLTemplate VERSIONS_URL_TEMPLATE = new URLTemplate("files/%s/versions");
037    private static final URLTemplate COPY_URL_TEMPLATE = new URLTemplate("files/%s/copy");
038    private static final URLTemplate ADD_COMMENT_URL_TEMPLATE = new URLTemplate("comments");
039    private static final URLTemplate GET_COMMENTS_URL_TEMPLATE = new URLTemplate("files/%s/comments");
040    private static final URLTemplate METADATA_URL_TEMPLATE = new URLTemplate("files/%s/metadata/%s");
041    private static final String DEFAULT_METADATA_TYPE = "properties";
042    private static final int BUFFER_SIZE = 8192;
043
044    /**
045     * Constructs a BoxFile for a file with a given ID.
046     * @param  api the API connection to be used by the file.
047     * @param  id  the ID of the file.
048     */
049    public BoxFile(BoxAPIConnection api, String id) {
050        super(api, id);
051    }
052
053    @Override
054    public BoxSharedLink createSharedLink(BoxSharedLink.Access access, Date unshareDate,
055        BoxSharedLink.Permissions permissions) {
056
057        BoxSharedLink sharedLink = new BoxSharedLink(access, unshareDate, permissions);
058        Info info = new Info();
059        info.setSharedLink(sharedLink);
060
061        this.updateInfo(info);
062        return info.getSharedLink();
063    }
064
065    /**
066     * Adds a comment to this file. The message can contain @mentions by using the string @[userid:username] anywhere
067     * within the message, where userid and username are the ID and username of the person being mentioned.
068     * @see    <a href="https://developers.box.com/docs/#comments-add-a-comment-to-an-item">the tagged_message field
069     *         for including @mentions.</a>
070     * @param  message the comment's message.
071     * @return information about the newly added comment.
072     */
073    public BoxComment.Info addComment(String message) {
074        JsonObject itemJSON = new JsonObject();
075        itemJSON.add("type", "file");
076        itemJSON.add("id", this.getID());
077
078        JsonObject requestJSON = new JsonObject();
079        requestJSON.add("item", itemJSON);
080        if (BoxComment.messageContainsMention(message)) {
081            requestJSON.add("tagged_message", message);
082        } else {
083            requestJSON.add("message", message);
084        }
085
086        URL url = ADD_COMMENT_URL_TEMPLATE.build(this.getAPI().getBaseURL());
087        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "POST");
088        request.setBody(requestJSON.toString());
089        BoxJSONResponse response = (BoxJSONResponse) request.send();
090        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
091
092        BoxComment addedComment = new BoxComment(this.getAPI(), responseJSON.get("id").asString());
093        return addedComment.new Info(responseJSON);
094    }
095
096    /**
097     * Downloads the contents of this file to a given OutputStream.
098     * @param output the stream to where the file will be written.
099     */
100    public void download(OutputStream output) {
101        this.download(output, null);
102    }
103
104    /**
105     * Downloads the contents of this file to a given OutputStream while reporting the progress to a ProgressListener.
106     * @param output   the stream to where the file will be written.
107     * @param listener a listener for monitoring the download's progress.
108     */
109    public void download(OutputStream output, ProgressListener listener) {
110        URL url = CONTENT_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
111        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET");
112        BoxAPIResponse response = request.send();
113        InputStream input = response.getBody(listener);
114
115        byte[] buffer = new byte[BUFFER_SIZE];
116        try {
117            int n = input.read(buffer);
118            while (n != -1) {
119                output.write(buffer, 0, n);
120                n = input.read(buffer);
121            }
122        } catch (IOException e) {
123            throw new BoxAPIException("Couldn't connect to the Box API due to a network error.", e);
124        } finally {
125            response.disconnect();
126        }
127    }
128
129    /**
130     * Downloads a part of this file's contents, starting at specified byte offset.
131     * @param output the stream to where the file will be written.
132     * @param offset the byte offset at which to start the download.
133     */
134    public void downloadRange(OutputStream output, long offset) {
135        this.downloadRange(output, offset, -1);
136    }
137
138    /**
139     * Downloads a part of this file's contents, starting at rangeStart and stopping at rangeEnd.
140     * @param output     the stream to where the file will be written.
141     * @param rangeStart the byte offset at which to start the download.
142     * @param rangeEnd   the byte offset at which to stop the download.
143     */
144    public void downloadRange(OutputStream output, long rangeStart, long rangeEnd) {
145        this.downloadRange(output, rangeStart, rangeEnd, null);
146    }
147
148    /**
149     * Downloads a part of this file's contents, starting at rangeStart and stopping at rangeEnd, while reporting the
150     * progress to a ProgressListener.
151     * @param output     the stream to where the file will be written.
152     * @param rangeStart the byte offset at which to start the download.
153     * @param rangeEnd   the byte offset at which to stop the download.
154     * @param listener   a listener for monitoring the download's progress.
155     */
156    public void downloadRange(OutputStream output, long rangeStart, long rangeEnd, ProgressListener listener) {
157        URL url = CONTENT_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
158        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET");
159        if (rangeEnd > 0) {
160            request.addHeader("Range", String.format("bytes=%s-%s", Long.toString(rangeStart),
161                Long.toString(rangeEnd)));
162        } else {
163            request.addHeader("Range", String.format("bytes=%s-", Long.toString(rangeStart)));
164        }
165
166        BoxAPIResponse response = request.send();
167        InputStream input = response.getBody(listener);
168
169        byte[] buffer = new byte[BUFFER_SIZE];
170        try {
171            int n = input.read(buffer);
172            while (n != -1) {
173                output.write(buffer, 0, n);
174                n = input.read(buffer);
175            }
176        } catch (IOException e) {
177            throw new BoxAPIException("Couldn't connect to the Box API due to a network error.", e);
178        } finally {
179            response.disconnect();
180        }
181    }
182
183    @Override
184    public BoxFile.Info copy(BoxFolder destination) {
185        return this.copy(destination, null);
186    }
187
188    @Override
189    public BoxFile.Info copy(BoxFolder destination, String newName) {
190        URL url = COPY_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
191
192        JsonObject parent = new JsonObject();
193        parent.add("id", destination.getID());
194
195        JsonObject copyInfo = new JsonObject();
196        copyInfo.add("parent", parent);
197        if (newName != null) {
198            copyInfo.add("name", newName);
199        }
200
201        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "POST");
202        request.setBody(copyInfo.toString());
203        BoxJSONResponse response = (BoxJSONResponse) request.send();
204        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
205        BoxFile copiedFile = new BoxFile(this.getAPI(), responseJSON.get("id").asString());
206        return copiedFile.new Info(responseJSON);
207    }
208
209    /**
210     * Deletes this file by moving it to the trash.
211     */
212    public void delete() {
213        URL url = FILE_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
214        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "DELETE");
215        BoxAPIResponse response = request.send();
216        response.disconnect();
217    }
218
219    @Override
220    public BoxItem.Info move(BoxFolder destination) {
221        return this.move(destination, null);
222    }
223
224    @Override
225    public BoxItem.Info move(BoxFolder destination, String newName) {
226        URL url = FILE_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
227        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT");
228
229        JsonObject parent = new JsonObject();
230        parent.add("id", destination.getID());
231
232        JsonObject updateInfo = new JsonObject();
233        updateInfo.add("parent", parent);
234        if (newName != null) {
235            updateInfo.add("name", newName);
236        }
237
238        request.setBody(updateInfo.toString());
239        BoxJSONResponse response = (BoxJSONResponse) request.send();
240        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
241        BoxFile movedFile = new BoxFile(this.getAPI(), responseJSON.get("id").asString());
242        return movedFile.new Info(responseJSON);
243    }
244
245    /**
246     * Renames this file.
247     * @param newName the new name of the file.
248     */
249    public void rename(String newName) {
250        URL url = FILE_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
251        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT");
252
253        JsonObject updateInfo = new JsonObject();
254        updateInfo.add("name", newName);
255
256        request.setBody(updateInfo.toString());
257        BoxAPIResponse response = request.send();
258        response.disconnect();
259    }
260
261    @Override
262    public BoxFile.Info getInfo() {
263        URL url = FILE_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
264        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET");
265        BoxJSONResponse response = (BoxJSONResponse) request.send();
266        return new Info(response.getJSON());
267    }
268
269    @Override
270    public BoxFile.Info getInfo(String... fields) {
271        String queryString = new QueryStringBuilder().appendParam("fields", fields).toString();
272        URL url = FILE_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), queryString, this.getID());
273
274        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET");
275        BoxJSONResponse response = (BoxJSONResponse) request.send();
276        return new Info(response.getJSON());
277    }
278
279    /**
280     * Updates the information about this file with any info fields that have been modified locally.
281     *
282     * <p>The only fields that will be updated are the ones that have been modified locally. For example, the following
283     * code won't update any information (or even send a network request) since none of the info's fields were
284     * changed:</p>
285     *
286     * <pre>BoxFile file = new File(api, id);
287     *BoxFile.Info info = file.getInfo();
288     *file.updateInfo(info);</pre>
289     *
290     * @param info the updated info.
291     */
292    public void updateInfo(BoxFile.Info info) {
293        URL url = FILE_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    /**
302     * Gets any previous versions of this file. Note that only users with premium accounts will be able to retrieve
303     * previous versions of their files.
304     * @return a list of previous file versions.
305     */
306    public Collection<BoxFileVersion> getVersions() {
307        URL url = VERSIONS_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
308        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET");
309        BoxJSONResponse response = (BoxJSONResponse) request.send();
310
311        JsonObject jsonObject = JsonObject.readFrom(response.getJSON());
312        JsonArray entries = jsonObject.get("entries").asArray();
313        Collection<BoxFileVersion> versions = new ArrayList<BoxFileVersion>();
314        for (JsonValue entry : entries) {
315            versions.add(new BoxFileVersion(this.getAPI(), entry.asObject(), this.getID()));
316        }
317
318        return versions;
319    }
320
321    /**
322     * Uploads a new version of this file, replacing the current version. Note that only users with premium accounts
323     * will be able to view and recover previous versions of the file.
324     * @param fileContent a stream containing the new file contents.
325     */
326    public void uploadVersion(InputStream fileContent) {
327        this.uploadVersion(fileContent, null);
328    }
329
330    /**
331     * Uploads a new version of this file, replacing the current version. Note that only users with premium accounts
332     * will be able to view and recover previous versions of the file.
333     * @param fileContent a stream containing the new file contents.
334     * @param modified    the date that the new version was modified.
335     */
336    public void uploadVersion(InputStream fileContent, Date modified) {
337        this.uploadVersion(fileContent, modified, 0, null);
338    }
339
340    /**
341     * Uploads a new version of this file, replacing the current version, while reporting the progress to a
342     * ProgressListener. Note that only users with premium accounts will be able to view and recover previous versions
343     * of the file.
344     * @param fileContent a stream containing the new file contents.
345     * @param modified    the date that the new version was modified.
346     * @param fileSize    the size of the file used for determining the progress of the upload.
347     * @param listener    a listener for monitoring the upload's progress.
348     */
349    public void uploadVersion(InputStream fileContent, Date modified, long fileSize, ProgressListener listener) {
350        URL uploadURL = CONTENT_URL_TEMPLATE.build(this.getAPI().getBaseUploadURL(), this.getID());
351        BoxMultipartRequest request = new BoxMultipartRequest(getAPI(), uploadURL);
352        if (fileSize > 0) {
353            request.setFile(fileContent, "", fileSize);
354        } else {
355            request.setFile(fileContent, "");
356        }
357
358        if (modified != null) {
359            request.putField("content_modified_at", modified);
360        }
361
362        BoxAPIResponse response;
363        if (listener == null) {
364            response = request.send();
365        } else {
366            response = request.send(listener);
367        }
368        response.disconnect();
369    }
370
371    /**
372     * Gets a list of any comments on this file.
373     * @return a list of comments on this file.
374     */
375    public List<BoxComment.Info> getComments() {
376        URL url = GET_COMMENTS_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
377        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET");
378        BoxJSONResponse response = (BoxJSONResponse) request.send();
379        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
380
381        int totalCount = responseJSON.get("total_count").asInt();
382        List<BoxComment.Info> comments = new ArrayList<BoxComment.Info>(totalCount);
383        JsonArray entries = responseJSON.get("entries").asArray();
384        for (JsonValue value : entries) {
385            JsonObject commentJSON = value.asObject();
386            BoxComment comment = new BoxComment(this.getAPI(), commentJSON.get("id").asString());
387            BoxComment.Info info = comment.new Info(commentJSON);
388            comments.add(info);
389        }
390
391        return comments;
392    }
393
394    /**
395     * Creates metadata on this file.
396     * @param metadata The new metadata values.
397     * @return the metadata returned from the server.
398     */
399    public Metadata createMetadata(Metadata metadata) {
400        return this.createMetadata(DEFAULT_METADATA_TYPE, metadata);
401    }
402
403    /**
404     * Creates the metadata of specified type.
405     * @param typeName the metadata type name.
406     * @param metadata the new metadata values.
407     * @return the metadata returned from the server.
408     */
409    public Metadata createMetadata(String typeName, Metadata metadata) {
410        URL url = METADATA_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), typeName);
411        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "POST");
412        request.addHeader("Content-Type", "application/json");
413        request.setBody(metadata.toString());
414        BoxJSONResponse response = (BoxJSONResponse) request.send();
415        return new Metadata(JsonObject.readFrom(response.getJSON()));
416    }
417
418    /**
419     * Gets the file properties metadata.
420     * @return the metadata returned from the server.
421     */
422    public Metadata getMetadata() {
423        return this.getMetadata(DEFAULT_METADATA_TYPE);
424    }
425
426    /**
427     * Gets the file metadata of specified type.
428     * @param typeName the metadata type name.
429     * @return the metadata returned from the server.
430     */
431    public Metadata getMetadata(String typeName) {
432        URL url = METADATA_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), typeName);
433        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET");
434        BoxJSONResponse response = (BoxJSONResponse) request.send();
435        return new Metadata(JsonObject.readFrom(response.getJSON()));
436    }
437
438    /**
439     * Updates the file metadata.
440     * @param metadata the new metadata values.
441     * @return the metadata returned from the server.
442     */
443    public Metadata updateMetadata(Metadata metadata) {
444        URL url = METADATA_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), metadata.getTypeName());
445        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "PUT");
446        request.addHeader("Content-Type", "application/json-patch+json");
447        request.setBody(metadata.getPatch());
448        BoxJSONResponse response = (BoxJSONResponse) request.send();
449        return new Metadata(JsonObject.readFrom(response.getJSON()));
450    }
451
452    /**
453     * Deletes the file properties metadata.
454     */
455    public void deleteMetadata() {
456        this.deleteMetadata(DEFAULT_METADATA_TYPE);
457    }
458
459    /**
460     * Deletes the file metadata of specified type.
461     * @param typeName the metadata type name.
462     */
463    public void deleteMetadata(String typeName) {
464        URL url = METADATA_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), typeName);
465        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "DELETE");
466        request.send();
467    }
468
469    /**
470     * Contains information about a BoxFile.
471     */
472    public class Info extends BoxItem.Info {
473        private String sha1;
474        private String versionNumber;
475        private long commentCount;
476        private EnumSet<Permission> permissions;
477        private String extension;
478        private boolean isPackage;
479        private BoxFileVersion version;
480
481        /**
482         * Constructs an empty Info object.
483         */
484        public Info() {
485            super();
486        }
487
488        /**
489         * Constructs an Info object by parsing information from a JSON string.
490         * @param  json the JSON string to parse.
491         */
492        public Info(String json) {
493            super(json);
494        }
495
496        /**
497         * Constructs an Info object using an already parsed JSON object.
498         * @param  jsonObject the parsed JSON object.
499         */
500        Info(JsonObject jsonObject) {
501            super(jsonObject);
502        }
503
504        @Override
505        public BoxFile getResource() {
506            return BoxFile.this;
507        }
508
509        /**
510         * Gets the SHA1 hash of the file.
511         * @return the SHA1 hash of the file.
512         */
513        public String getSha1() {
514            return this.sha1;
515        }
516
517        /**
518         * Gets the current version number of the file.
519         * @return the current version number of the file.
520         */
521        public String getVersionNumber() {
522            return this.versionNumber;
523        }
524
525        /**
526         * Gets the number of comments on the file.
527         * @return the number of comments on the file.
528         */
529        public long getCommentCount() {
530            return this.commentCount;
531        }
532
533        /**
534         * Gets the permissions that the current user has on the file.
535         * @return the permissions that the current user has on the file.
536         */
537        public EnumSet<Permission> getPermissions() {
538            return this.permissions;
539        }
540
541        /**
542         * Gets the extension suffix of the file, excluding the dot.
543         * @return the extension of the file.
544         */
545        public String getExtension() {
546            return this.extension;
547        }
548
549        /**
550         * Gets whether or not the file is an OSX package.
551         * @return true if the file is an OSX package; otherwise false.
552         */
553        public boolean getIsPackage() {
554            return this.isPackage;
555        }
556
557        /**
558         * Gets the current version details of the file.
559         * @return the current version details of the file.
560         */
561        public BoxFileVersion getVersion() {
562            return this.version;
563        }
564
565        @Override
566        protected void parseJSONMember(JsonObject.Member member) {
567            super.parseJSONMember(member);
568
569            String memberName = member.getName();
570            JsonValue value = member.getValue();
571            if (memberName.equals("sha1")) {
572                this.sha1 = value.asString();
573            } else if (memberName.equals("version_number")) {
574                this.versionNumber = value.asString();
575            } else if (memberName.equals("comment_count")) {
576                this.commentCount = value.asLong();
577            } else if (memberName.equals("permissions")) {
578                this.permissions = this.parsePermissions(value.asObject());
579            } else if (memberName.equals("extension")) {
580                this.extension = value.asString();
581            } else if (memberName.equals("is_package")) {
582                this.isPackage = value.asBoolean();
583            } else if (memberName.equals("file_version")) {
584                this.version = this.parseFileVersion(value.asObject());
585            }
586        }
587
588        private EnumSet<Permission> parsePermissions(JsonObject jsonObject) {
589            EnumSet<Permission> permissions = EnumSet.noneOf(Permission.class);
590            for (JsonObject.Member member : jsonObject) {
591                JsonValue value = member.getValue();
592                if (value.isNull() || !value.asBoolean()) {
593                    continue;
594                }
595
596                String memberName = member.getName();
597                if (memberName.equals("can_download")) {
598                    permissions.add(Permission.CAN_DOWNLOAD);
599                } else if (memberName.equals("can_upload")) {
600                    permissions.add(Permission.CAN_UPLOAD);
601                } else if (memberName.equals("can_rename")) {
602                    permissions.add(Permission.CAN_RENAME);
603                } else if (memberName.equals("can_delete")) {
604                    permissions.add(Permission.CAN_DELETE);
605                } else if (memberName.equals("can_share")) {
606                    permissions.add(Permission.CAN_SHARE);
607                } else if (memberName.equals("can_set_share_access")) {
608                    permissions.add(Permission.CAN_SET_SHARE_ACCESS);
609                } else if (memberName.equals("can_preview")) {
610                    permissions.add(Permission.CAN_PREVIEW);
611                } else if (memberName.equals("can_comment")) {
612                    permissions.add(Permission.CAN_COMMENT);
613                }
614            }
615
616            return permissions;
617        }
618
619        private BoxFileVersion parseFileVersion(JsonObject jsonObject) {
620            return new BoxFileVersion(BoxFile.this.getAPI(), jsonObject, BoxFile.this.getID());
621        }
622    }
623
624    /**
625     * Enumerates the possible permissions that a user can have on a file.
626     */
627    public enum Permission {
628        /**
629         * The user can download the file.
630         */
631        CAN_DOWNLOAD ("can_download"),
632
633        /**
634         * The user can upload new versions of the file.
635         */
636        CAN_UPLOAD ("can_upload"),
637
638        /**
639         * The user can rename the file.
640         */
641        CAN_RENAME ("can_rename"),
642
643        /**
644         * The user can delete the file.
645         */
646        CAN_DELETE ("can_delete"),
647
648        /**
649         * The user can share the file.
650         */
651        CAN_SHARE ("can_share"),
652
653        /**
654         * The user can set the access level for shared links to the file.
655         */
656        CAN_SET_SHARE_ACCESS ("can_set_share_access"),
657
658        /**
659         * The user can preview the file.
660         */
661        CAN_PREVIEW ("can_preview"),
662
663        /**
664         * The user can comment on the file.
665         */
666        CAN_COMMENT ("can_comment");
667
668        private final String jsonValue;
669
670        private Permission(String jsonValue) {
671            this.jsonValue = jsonValue;
672        }
673
674        static Permission fromJSONValue(String jsonValue) {
675            return Permission.valueOf(jsonValue.toUpperCase());
676        }
677
678        String toJSONValue() {
679            return this.jsonValue;
680        }
681    }
682}