001package com.box.sdk; 002 003import java.io.ByteArrayOutputStream; 004import java.io.IOException; 005import java.io.InputStream; 006import java.io.OutputStream; 007import java.net.MalformedURLException; 008import java.net.URL; 009import java.util.ArrayList; 010import java.util.Collection; 011import java.util.Date; 012import java.util.EnumSet; 013import java.util.List; 014 015import com.eclipsesource.json.JsonArray; 016import com.eclipsesource.json.JsonObject; 017import com.eclipsesource.json.JsonValue; 018 019 020/** 021 * Represents an individual file on Box. This class can be used to download a file's contents, upload new versions, and 022 * perform other common file operations (move, copy, delete, etc.). 023 * 024 * <p>Unless otherwise noted, the methods in this class can throw an unchecked {@link BoxAPIException} (unchecked 025 * meaning that the compiler won't force you to handle it) if an error occurs. If you wish to implement custom error 026 * handling for errors related to the Box REST API, you should capture this exception explicitly.</p> 027 */ 028public class BoxFile extends BoxItem { 029 030 /** 031 * An array of all possible file fields that can be requested when calling {@link #getInfo()}. 032 */ 033 public static final String[] ALL_FIELDS = {"type", "id", "sequence_id", "etag", "sha1", "name", 034 "description", "size", "path_collection", "created_at", "modified_at", "trashed_at", "purged_at", 035 "content_created_at", "content_modified_at", "created_by", "modified_by", "owned_by", "shared_link", "parent", 036 "item_status", "version_number", "comment_count", "permissions", "tags", "lock", "extension", "is_package", 037 "file_version"}; 038 039 /** 040 * Used to specify what filetype to request for a file thumbnail. 041 */ 042 public enum ThumbnailFileType { 043 /** 044 * PNG image format. 045 */ 046 PNG, 047 048 /** 049 * JPG image format. 050 */ 051 JPG 052 } 053 054 private static final URLTemplate FILE_URL_TEMPLATE = new URLTemplate("files/%s"); 055 private static final URLTemplate CONTENT_URL_TEMPLATE = new URLTemplate("files/%s/content"); 056 private static final URLTemplate VERSIONS_URL_TEMPLATE = new URLTemplate("files/%s/versions"); 057 private static final URLTemplate COPY_URL_TEMPLATE = new URLTemplate("files/%s/copy"); 058 private static final URLTemplate ADD_COMMENT_URL_TEMPLATE = new URLTemplate("comments"); 059 private static final URLTemplate GET_COMMENTS_URL_TEMPLATE = new URLTemplate("files/%s/comments"); 060 private static final URLTemplate METADATA_URL_TEMPLATE = new URLTemplate("files/%s/metadata/%s"); 061 private static final URLTemplate ADD_TASK_URL_TEMPLATE = new URLTemplate("tasks"); 062 private static final URLTemplate GET_TASKS_URL_TEMPLATE = new URLTemplate("files/%s/tasks"); 063 private static final URLTemplate GET_THUMBNAIL_PNG_TEMPLATE = new URLTemplate("files/%s/thumbnail.png"); 064 private static final URLTemplate GET_THUMBNAIL_JPG_TEMPLATE = new URLTemplate("files/%s/thumbnail.jpg"); 065 private static final String DEFAULT_METADATA_TYPE = "properties"; 066 private static final int BUFFER_SIZE = 8192; 067 068 069 /** 070 * Constructs a BoxFile for a file with a given ID. 071 * @param api the API connection to be used by the file. 072 * @param id the ID of the file. 073 */ 074 public BoxFile(BoxAPIConnection api, String id) { 075 super(api, id); 076 } 077 078 @Override 079 public BoxSharedLink createSharedLink(BoxSharedLink.Access access, Date unshareDate, 080 BoxSharedLink.Permissions permissions) { 081 082 BoxSharedLink sharedLink = new BoxSharedLink(access, unshareDate, permissions); 083 Info info = new Info(); 084 info.setSharedLink(sharedLink); 085 086 this.updateInfo(info); 087 return info.getSharedLink(); 088 } 089 090 /** 091 * Adds a comment to this file. The message can contain @mentions by using the string @[userid:username] anywhere 092 * within the message, where userid and username are the ID and username of the person being mentioned. 093 * @see <a href="https://developers.box.com/docs/#comments-add-a-comment-to-an-item">the tagged_message field 094 * for including @mentions.</a> 095 * @param message the comment's message. 096 * @return information about the newly added comment. 097 */ 098 public BoxComment.Info addComment(String message) { 099 JsonObject itemJSON = new JsonObject(); 100 itemJSON.add("type", "file"); 101 itemJSON.add("id", this.getID()); 102 103 JsonObject requestJSON = new JsonObject(); 104 requestJSON.add("item", itemJSON); 105 if (BoxComment.messageContainsMention(message)) { 106 requestJSON.add("tagged_message", message); 107 } else { 108 requestJSON.add("message", message); 109 } 110 111 URL url = ADD_COMMENT_URL_TEMPLATE.build(this.getAPI().getBaseURL()); 112 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "POST"); 113 request.setBody(requestJSON.toString()); 114 BoxJSONResponse response = (BoxJSONResponse) request.send(); 115 JsonObject responseJSON = JsonObject.readFrom(response.getJSON()); 116 117 BoxComment addedComment = new BoxComment(this.getAPI(), responseJSON.get("id").asString()); 118 return addedComment.new Info(responseJSON); 119 } 120 121 /** 122 * Adds a new task to this file. The task can have an optional message to include, and a due date. 123 * @param action the action the task assignee will be prompted to do. 124 * @param message an optional message to include with the task. 125 * @param dueAt the day at which this task is due. 126 * @return information about the newly added task. 127 */ 128 public BoxTask.Info addTask(BoxTask.Action action, String message, Date dueAt) { 129 JsonObject itemJSON = new JsonObject(); 130 itemJSON.add("type", "file"); 131 itemJSON.add("id", this.getID()); 132 133 JsonObject requestJSON = new JsonObject(); 134 requestJSON.add("item", itemJSON); 135 requestJSON.add("action", action.toJSONString()); 136 137 if (message != null && !message.isEmpty()) { 138 requestJSON.add("message", message); 139 } 140 141 if (dueAt != null) { 142 requestJSON.add("due_at", dueAt.toString()); 143 } 144 145 URL url = ADD_TASK_URL_TEMPLATE.build(this.getAPI().getBaseURL()); 146 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "POST"); 147 request.setBody(requestJSON.toString()); 148 BoxJSONResponse response = (BoxJSONResponse) request.send(); 149 JsonObject responseJSON = JsonObject.readFrom(response.getJSON()); 150 151 BoxTask addedTask = new BoxTask(this.getAPI(), responseJSON.get("id").asString()); 152 return addedTask.new Info(responseJSON); 153 } 154 155 /** 156 * Gets an expiring URL for downloading a file directly from Box. This can be user, 157 * for example, for sending as a redirect to a browser to cause the browser 158 * to download the file directly from Box. 159 * @return the temporary download URL 160 */ 161 public URL getDownloadURL() { 162 URL url = CONTENT_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 163 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 164 request.setFollowRedirects(false); 165 166 BoxRedirectResponse response = (BoxRedirectResponse) request.send(); 167 168 return response.getRedirectURL(); 169 } 170 171 /** 172 * Downloads the contents of this file to a given OutputStream. 173 * @param output the stream to where the file will be written. 174 */ 175 public void download(OutputStream output) { 176 this.download(output, null); 177 } 178 179 /** 180 * Downloads the contents of this file to a given OutputStream while reporting the progress to a ProgressListener. 181 * @param output the stream to where the file will be written. 182 * @param listener a listener for monitoring the download's progress. 183 */ 184 public void download(OutputStream output, ProgressListener listener) { 185 URL url = CONTENT_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 186 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 187 BoxAPIResponse response = request.send(); 188 InputStream input = response.getBody(listener); 189 190 byte[] buffer = new byte[BUFFER_SIZE]; 191 try { 192 int n = input.read(buffer); 193 while (n != -1) { 194 output.write(buffer, 0, n); 195 n = input.read(buffer); 196 } 197 } catch (IOException e) { 198 throw new BoxAPIException("Couldn't connect to the Box API due to a network error.", e); 199 } finally { 200 response.disconnect(); 201 } 202 } 203 204 /** 205 * Downloads a part of this file's contents, starting at specified byte offset. 206 * @param output the stream to where the file will be written. 207 * @param offset the byte offset at which to start the download. 208 */ 209 public void downloadRange(OutputStream output, long offset) { 210 this.downloadRange(output, offset, -1); 211 } 212 213 /** 214 * Downloads a part of this file's contents, starting at rangeStart and stopping at rangeEnd. 215 * @param output the stream to where the file will be written. 216 * @param rangeStart the byte offset at which to start the download. 217 * @param rangeEnd the byte offset at which to stop the download. 218 */ 219 public void downloadRange(OutputStream output, long rangeStart, long rangeEnd) { 220 this.downloadRange(output, rangeStart, rangeEnd, null); 221 } 222 223 /** 224 * Downloads a part of this file's contents, starting at rangeStart and stopping at rangeEnd, while reporting the 225 * progress to a ProgressListener. 226 * @param output the stream to where the file will be written. 227 * @param rangeStart the byte offset at which to start the download. 228 * @param rangeEnd the byte offset at which to stop the download. 229 * @param listener a listener for monitoring the download's progress. 230 */ 231 public void downloadRange(OutputStream output, long rangeStart, long rangeEnd, ProgressListener listener) { 232 URL url = CONTENT_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 233 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 234 if (rangeEnd > 0) { 235 request.addHeader("Range", String.format("bytes=%s-%s", Long.toString(rangeStart), 236 Long.toString(rangeEnd))); 237 } else { 238 request.addHeader("Range", String.format("bytes=%s-", Long.toString(rangeStart))); 239 } 240 241 BoxAPIResponse response = request.send(); 242 InputStream input = response.getBody(listener); 243 244 byte[] buffer = new byte[BUFFER_SIZE]; 245 try { 246 int n = input.read(buffer); 247 while (n != -1) { 248 output.write(buffer, 0, n); 249 n = input.read(buffer); 250 } 251 } catch (IOException e) { 252 throw new BoxAPIException("Couldn't connect to the Box API due to a network error.", e); 253 } finally { 254 response.disconnect(); 255 } 256 } 257 258 @Override 259 public BoxFile.Info copy(BoxFolder destination) { 260 return this.copy(destination, null); 261 } 262 263 @Override 264 public BoxFile.Info copy(BoxFolder destination, String newName) { 265 URL url = COPY_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 266 267 JsonObject parent = new JsonObject(); 268 parent.add("id", destination.getID()); 269 270 JsonObject copyInfo = new JsonObject(); 271 copyInfo.add("parent", parent); 272 if (newName != null) { 273 copyInfo.add("name", newName); 274 } 275 276 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "POST"); 277 request.setBody(copyInfo.toString()); 278 BoxJSONResponse response = (BoxJSONResponse) request.send(); 279 JsonObject responseJSON = JsonObject.readFrom(response.getJSON()); 280 BoxFile copiedFile = new BoxFile(this.getAPI(), responseJSON.get("id").asString()); 281 return copiedFile.new Info(responseJSON); 282 } 283 284 /** 285 * Deletes this file by moving it to the trash. 286 */ 287 public void delete() { 288 URL url = FILE_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 289 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "DELETE"); 290 BoxAPIResponse response = request.send(); 291 response.disconnect(); 292 } 293 294 @Override 295 public BoxItem.Info move(BoxFolder destination) { 296 return this.move(destination, null); 297 } 298 299 @Override 300 public BoxItem.Info move(BoxFolder destination, String newName) { 301 URL url = FILE_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 302 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT"); 303 304 JsonObject parent = new JsonObject(); 305 parent.add("id", destination.getID()); 306 307 JsonObject updateInfo = new JsonObject(); 308 updateInfo.add("parent", parent); 309 if (newName != null) { 310 updateInfo.add("name", newName); 311 } 312 313 request.setBody(updateInfo.toString()); 314 BoxJSONResponse response = (BoxJSONResponse) request.send(); 315 JsonObject responseJSON = JsonObject.readFrom(response.getJSON()); 316 BoxFile movedFile = new BoxFile(this.getAPI(), responseJSON.get("id").asString()); 317 return movedFile.new Info(responseJSON); 318 } 319 320 /** 321 * Renames this file. 322 * @param newName the new name of the file. 323 */ 324 public void rename(String newName) { 325 URL url = FILE_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 326 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT"); 327 328 JsonObject updateInfo = new JsonObject(); 329 updateInfo.add("name", newName); 330 331 request.setBody(updateInfo.toString()); 332 BoxAPIResponse response = request.send(); 333 response.disconnect(); 334 } 335 336 @Override 337 public BoxFile.Info getInfo() { 338 URL url = FILE_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 339 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 340 BoxJSONResponse response = (BoxJSONResponse) request.send(); 341 return new Info(response.getJSON()); 342 } 343 344 @Override 345 public BoxFile.Info getInfo(String... fields) { 346 String queryString = new QueryStringBuilder().appendParam("fields", fields).toString(); 347 URL url = FILE_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), queryString, this.getID()); 348 349 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 350 BoxJSONResponse response = (BoxJSONResponse) request.send(); 351 return new Info(response.getJSON()); 352 } 353 354 /** 355 * Updates the information about this file with any info fields that have been modified locally. 356 * 357 * <p>The only fields that will be updated are the ones that have been modified locally. For example, the following 358 * code won't update any information (or even send a network request) since none of the info's fields were 359 * changed:</p> 360 * 361 * <pre>BoxFile file = new File(api, id); 362 *BoxFile.Info info = file.getInfo(); 363 *file.updateInfo(info);</pre> 364 * 365 * @param info the updated info. 366 */ 367 public void updateInfo(BoxFile.Info info) { 368 URL url = FILE_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 369 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT"); 370 request.setBody(info.getPendingChanges()); 371 BoxJSONResponse response = (BoxJSONResponse) request.send(); 372 JsonObject jsonObject = JsonObject.readFrom(response.getJSON()); 373 info.update(jsonObject); 374 } 375 376 /** 377 * Gets any previous versions of this file. Note that only users with premium accounts will be able to retrieve 378 * previous versions of their files. 379 * @return a list of previous file versions. 380 */ 381 public Collection<BoxFileVersion> getVersions() { 382 URL url = VERSIONS_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 383 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 384 BoxJSONResponse response = (BoxJSONResponse) request.send(); 385 386 JsonObject jsonObject = JsonObject.readFrom(response.getJSON()); 387 JsonArray entries = jsonObject.get("entries").asArray(); 388 Collection<BoxFileVersion> versions = new ArrayList<BoxFileVersion>(); 389 for (JsonValue entry : entries) { 390 versions.add(new BoxFileVersion(this.getAPI(), entry.asObject(), this.getID())); 391 } 392 393 return versions; 394 } 395 396 /** 397 * Checks if the file can be successfully uploaded by using the preflight check. 398 * @param name the name to give the uploaded file or null to use existing name. 399 * @param fileSize the size of the file used for account capacity calculations. 400 * @param parentID the ID of the parent folder that the new version is being uploaded to. 401 */ 402 public void canUploadVersion(String name, long fileSize, String parentID) { 403 URL url = CONTENT_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 404 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "OPTIONS"); 405 406 JsonObject parent = new JsonObject(); 407 parent.add("id", parentID); 408 409 JsonObject preflightInfo = new JsonObject(); 410 preflightInfo.add("parent", parent); 411 if (name != null) { 412 preflightInfo.add("name", name); 413 } 414 415 preflightInfo.add("size", fileSize); 416 417 request.setBody(preflightInfo.toString()); 418 BoxAPIResponse response = request.send(); 419 response.disconnect(); 420 } 421 422 /** 423 * Uploads a new version of this file, replacing the current version. Note that only users with premium accounts 424 * will be able to view and recover previous versions of the file. 425 * @param fileContent a stream containing the new file contents. 426 */ 427 public void uploadVersion(InputStream fileContent) { 428 this.uploadVersion(fileContent, null); 429 } 430 431 /** 432 * Uploads a new version of this file, replacing the current version. Note that only users with premium accounts 433 * will be able to view and recover previous versions of the file. 434 * @param fileContent a stream containing the new file contents. 435 * @param modified the date that the new version was modified. 436 */ 437 public void uploadVersion(InputStream fileContent, Date modified) { 438 this.uploadVersion(fileContent, modified, 0, null); 439 } 440 441 /** 442 * Uploads a new version of this file, replacing the current version, while reporting the progress to a 443 * ProgressListener. Note that only users with premium accounts will be able to view and recover previous versions 444 * of the file. 445 * @param fileContent a stream containing the new file contents. 446 * @param modified the date that the new version was modified. 447 * @param fileSize the size of the file used for determining the progress of the upload. 448 * @param listener a listener for monitoring the upload's progress. 449 */ 450 public void uploadVersion(InputStream fileContent, Date modified, long fileSize, ProgressListener listener) { 451 URL uploadURL = CONTENT_URL_TEMPLATE.build(this.getAPI().getBaseUploadURL(), this.getID()); 452 BoxMultipartRequest request = new BoxMultipartRequest(getAPI(), uploadURL); 453 if (fileSize > 0) { 454 request.setFile(fileContent, "", fileSize); 455 } else { 456 request.setFile(fileContent, ""); 457 } 458 459 if (modified != null) { 460 request.putField("content_modified_at", modified); 461 } 462 463 BoxAPIResponse response; 464 if (listener == null) { 465 response = request.send(); 466 } else { 467 response = request.send(listener); 468 } 469 response.disconnect(); 470 } 471 472 /** 473 * Gets an expiring URL for creating an embedded preview session. The URL will expire after 60 seconds and the 474 * preview session will expire after 60 minutes. 475 * @return the expiring preview link 476 */ 477 public URL getPreviewLink() { 478 BoxFile.Info info = this.getInfo("expiring_embed_link"); 479 480 return info.getPreviewLink(); 481 } 482 483 484 /** 485 * Retrieves a thumbnail, or smaller image representation, of this file. Sizes of 32x32, 64x64, 128x128, 486 * and 256x256 can be returned in the .png format and sizes of 32x32, 94x94, 160x160, and 320x320 can be returned 487 * in the .jpg format. 488 * @param fileType either PNG of JPG 489 * @param minWidth minimum width 490 * @param minHeight minimum height 491 * @param maxWidth maximum width 492 * @param maxHeight maximum height 493 * @return the byte array of the thumbnail image 494 */ 495 public byte[] getThumbnail(ThumbnailFileType fileType, int minWidth, int minHeight, int maxWidth, int maxHeight) { 496 QueryStringBuilder builder = new QueryStringBuilder(); 497 builder.appendParam("min_width", minWidth); 498 builder.appendParam("min_height", minHeight); 499 builder.appendParam("max_width", maxWidth); 500 builder.appendParam("max_height", maxHeight); 501 502 URLTemplate template; 503 if (fileType == ThumbnailFileType.PNG) { 504 template = GET_THUMBNAIL_PNG_TEMPLATE; 505 } else if (fileType == ThumbnailFileType.JPG) { 506 template = GET_THUMBNAIL_JPG_TEMPLATE; 507 } else { 508 throw new BoxAPIException("Unsupported thumbnail file type"); 509 } 510 URL url = template.buildWithQuery(this.getAPI().getBaseURL(), builder.toString(), this.getID()); 511 512 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 513 BoxAPIResponse response = request.send(); 514 515 ByteArrayOutputStream thumbOut = new ByteArrayOutputStream(); 516 InputStream body = response.getBody(); 517 byte[] buffer = new byte[BUFFER_SIZE]; 518 try { 519 int n = body.read(buffer); 520 while (n != -1) { 521 thumbOut.write(buffer, 0, n); 522 n = body.read(buffer); 523 } 524 } catch (IOException e) { 525 throw new BoxAPIException("Error reading thumbnail bytes from response body", e); 526 } finally { 527 response.disconnect(); 528 } 529 530 return thumbOut.toByteArray(); 531 } 532 533 /** 534 * Gets a list of any comments on this file. 535 * @return a list of comments on this file. 536 */ 537 public List<BoxComment.Info> getComments() { 538 URL url = GET_COMMENTS_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 539 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 540 BoxJSONResponse response = (BoxJSONResponse) request.send(); 541 JsonObject responseJSON = JsonObject.readFrom(response.getJSON()); 542 543 int totalCount = responseJSON.get("total_count").asInt(); 544 List<BoxComment.Info> comments = new ArrayList<BoxComment.Info>(totalCount); 545 JsonArray entries = responseJSON.get("entries").asArray(); 546 for (JsonValue value : entries) { 547 JsonObject commentJSON = value.asObject(); 548 BoxComment comment = new BoxComment(this.getAPI(), commentJSON.get("id").asString()); 549 BoxComment.Info info = comment.new Info(commentJSON); 550 comments.add(info); 551 } 552 553 return comments; 554 } 555 556 /** 557 * Gets a list of any tasks on this file. 558 * @return a list of tasks on this file. 559 */ 560 public List<BoxTask.Info> getTasks() { 561 URL url = GET_TASKS_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 562 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 563 BoxJSONResponse response = (BoxJSONResponse) request.send(); 564 JsonObject responseJSON = JsonObject.readFrom(response.getJSON()); 565 566 int totalCount = responseJSON.get("total_count").asInt(); 567 List<BoxTask.Info> tasks = new ArrayList<BoxTask.Info>(totalCount); 568 JsonArray entries = responseJSON.get("entries").asArray(); 569 for (JsonValue value : entries) { 570 JsonObject taskJSON = value.asObject(); 571 BoxTask task = new BoxTask(this.getAPI(), taskJSON.get("id").asString()); 572 BoxTask.Info info = task.new Info(taskJSON); 573 tasks.add(info); 574 } 575 576 return tasks; 577 } 578 579 /** 580 * Creates metadata on this file. 581 * @param metadata The new metadata values. 582 * @return the metadata returned from the server. 583 */ 584 public Metadata createMetadata(Metadata metadata) { 585 return this.createMetadata(DEFAULT_METADATA_TYPE, metadata); 586 } 587 588 /** 589 * Creates the metadata of specified type. 590 * @param typeName the metadata type name. 591 * @param metadata the new metadata values. 592 * @return the metadata returned from the server. 593 */ 594 public Metadata createMetadata(String typeName, Metadata metadata) { 595 URL url = METADATA_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), typeName); 596 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "POST"); 597 request.addHeader("Content-Type", "application/json"); 598 request.setBody(metadata.toString()); 599 BoxJSONResponse response = (BoxJSONResponse) request.send(); 600 return new Metadata(JsonObject.readFrom(response.getJSON())); 601 } 602 603 /** 604 * Gets the file properties metadata. 605 * @return the metadata returned from the server. 606 */ 607 public Metadata getMetadata() { 608 return this.getMetadata(DEFAULT_METADATA_TYPE); 609 } 610 611 /** 612 * Gets the file metadata of specified type. 613 * @param typeName the metadata type name. 614 * @return the metadata returned from the server. 615 */ 616 public Metadata getMetadata(String typeName) { 617 URL url = METADATA_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), typeName); 618 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 619 BoxJSONResponse response = (BoxJSONResponse) request.send(); 620 return new Metadata(JsonObject.readFrom(response.getJSON())); 621 } 622 623 /** 624 * Updates the file metadata. 625 * @param metadata the new metadata values. 626 * @return the metadata returned from the server. 627 */ 628 public Metadata updateMetadata(Metadata metadata) { 629 URL url = METADATA_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), metadata.getTypeName()); 630 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "PUT"); 631 request.addHeader("Content-Type", "application/json-patch+json"); 632 request.setBody(metadata.getPatch()); 633 BoxJSONResponse response = (BoxJSONResponse) request.send(); 634 return new Metadata(JsonObject.readFrom(response.getJSON())); 635 } 636 637 /** 638 * Deletes the file properties metadata. 639 */ 640 public void deleteMetadata() { 641 this.deleteMetadata(DEFAULT_METADATA_TYPE); 642 } 643 644 /** 645 * Deletes the file metadata of specified type. 646 * @param typeName the metadata type name. 647 */ 648 public void deleteMetadata(String typeName) { 649 URL url = METADATA_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), typeName); 650 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "DELETE"); 651 request.send(); 652 } 653 654 /** 655 * Contains information about a BoxFile. 656 */ 657 public class Info extends BoxItem.Info { 658 private String sha1; 659 private String versionNumber; 660 private long commentCount; 661 private EnumSet<Permission> permissions; 662 private String extension; 663 private boolean isPackage; 664 private BoxFileVersion version; 665 private URL previewLink; 666 667 /** 668 * Constructs an empty Info object. 669 */ 670 public Info() { 671 super(); 672 } 673 674 /** 675 * Constructs an Info object by parsing information from a JSON string. 676 * @param json the JSON string to parse. 677 */ 678 public Info(String json) { 679 super(json); 680 } 681 682 /** 683 * Constructs an Info object using an already parsed JSON object. 684 * @param jsonObject the parsed JSON object. 685 */ 686 Info(JsonObject jsonObject) { 687 super(jsonObject); 688 } 689 690 @Override 691 public BoxFile getResource() { 692 return BoxFile.this; 693 } 694 695 /** 696 * Gets the SHA1 hash of the file. 697 * @return the SHA1 hash of the file. 698 */ 699 public String getSha1() { 700 return this.sha1; 701 } 702 703 /** 704 * Gets the current version number of the file. 705 * @return the current version number of the file. 706 */ 707 public String getVersionNumber() { 708 return this.versionNumber; 709 } 710 711 /** 712 * Gets the number of comments on the file. 713 * @return the number of comments on the file. 714 */ 715 public long getCommentCount() { 716 return this.commentCount; 717 } 718 719 /** 720 * Gets the permissions that the current user has on the file. 721 * @return the permissions that the current user has on the file. 722 */ 723 public EnumSet<Permission> getPermissions() { 724 return this.permissions; 725 } 726 727 /** 728 * Gets the extension suffix of the file, excluding the dot. 729 * @return the extension of the file. 730 */ 731 public String getExtension() { 732 return this.extension; 733 } 734 735 /** 736 * Gets whether or not the file is an OSX package. 737 * @return true if the file is an OSX package; otherwise false. 738 */ 739 public boolean getIsPackage() { 740 return this.isPackage; 741 } 742 743 /** 744 * Gets the current version details of the file. 745 * @return the current version details of the file. 746 */ 747 public BoxFileVersion getVersion() { 748 return this.version; 749 } 750 751 /** 752 * Gets the current expiring preview link. 753 * @return the expiring preview link 754 */ 755 public URL getPreviewLink() { 756 return this.previewLink; 757 } 758 759 @Override 760 protected void parseJSONMember(JsonObject.Member member) { 761 super.parseJSONMember(member); 762 763 String memberName = member.getName(); 764 JsonValue value = member.getValue(); 765 if (memberName.equals("sha1")) { 766 this.sha1 = value.asString(); 767 } else if (memberName.equals("version_number")) { 768 this.versionNumber = value.asString(); 769 } else if (memberName.equals("comment_count")) { 770 this.commentCount = value.asLong(); 771 } else if (memberName.equals("permissions")) { 772 this.permissions = this.parsePermissions(value.asObject()); 773 } else if (memberName.equals("extension")) { 774 this.extension = value.asString(); 775 } else if (memberName.equals("is_package")) { 776 this.isPackage = value.asBoolean(); 777 } else if (memberName.equals("file_version")) { 778 this.version = this.parseFileVersion(value.asObject()); 779 } else if (memberName.equals("expiring_embed_link")) { 780 try { 781 String urlString = member.getValue().asObject().get("url").asString(); 782 this.previewLink = new URL(urlString); 783 } catch (MalformedURLException e) { 784 throw new BoxAPIException("Couldn't parse expiring_embed_link/url for file", e); 785 } 786 } 787 } 788 789 private EnumSet<Permission> parsePermissions(JsonObject jsonObject) { 790 EnumSet<Permission> permissions = EnumSet.noneOf(Permission.class); 791 for (JsonObject.Member member : jsonObject) { 792 JsonValue value = member.getValue(); 793 if (value.isNull() || !value.asBoolean()) { 794 continue; 795 } 796 797 String memberName = member.getName(); 798 if (memberName.equals("can_download")) { 799 permissions.add(Permission.CAN_DOWNLOAD); 800 } else if (memberName.equals("can_upload")) { 801 permissions.add(Permission.CAN_UPLOAD); 802 } else if (memberName.equals("can_rename")) { 803 permissions.add(Permission.CAN_RENAME); 804 } else if (memberName.equals("can_delete")) { 805 permissions.add(Permission.CAN_DELETE); 806 } else if (memberName.equals("can_share")) { 807 permissions.add(Permission.CAN_SHARE); 808 } else if (memberName.equals("can_set_share_access")) { 809 permissions.add(Permission.CAN_SET_SHARE_ACCESS); 810 } else if (memberName.equals("can_preview")) { 811 permissions.add(Permission.CAN_PREVIEW); 812 } else if (memberName.equals("can_comment")) { 813 permissions.add(Permission.CAN_COMMENT); 814 } 815 } 816 817 return permissions; 818 } 819 820 private BoxFileVersion parseFileVersion(JsonObject jsonObject) { 821 return new BoxFileVersion(BoxFile.this.getAPI(), jsonObject, BoxFile.this.getID()); 822 } 823 } 824 825 /** 826 * Enumerates the possible permissions that a user can have on a file. 827 */ 828 public enum Permission { 829 /** 830 * The user can download the file. 831 */ 832 CAN_DOWNLOAD ("can_download"), 833 834 /** 835 * The user can upload new versions of the file. 836 */ 837 CAN_UPLOAD ("can_upload"), 838 839 /** 840 * The user can rename the file. 841 */ 842 CAN_RENAME ("can_rename"), 843 844 /** 845 * The user can delete the file. 846 */ 847 CAN_DELETE ("can_delete"), 848 849 /** 850 * The user can share the file. 851 */ 852 CAN_SHARE ("can_share"), 853 854 /** 855 * The user can set the access level for shared links to the file. 856 */ 857 CAN_SET_SHARE_ACCESS ("can_set_share_access"), 858 859 /** 860 * The user can preview the file. 861 */ 862 CAN_PREVIEW ("can_preview"), 863 864 /** 865 * The user can comment on the file. 866 */ 867 CAN_COMMENT ("can_comment"); 868 869 private final String jsonValue; 870 871 private Permission(String jsonValue) { 872 this.jsonValue = jsonValue; 873 } 874 875 static Permission fromJSONValue(String jsonValue) { 876 return Permission.valueOf(jsonValue.toUpperCase()); 877 } 878 879 String toJSONValue() { 880 return this.jsonValue; 881 } 882 } 883}