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