001package com.box.sdk; 002 003import static com.box.sdk.BinaryBodyUtils.writeStream; 004import static com.box.sdk.BinaryBodyUtils.writeStreamWithContentLength; 005import static com.box.sdk.http.ContentType.APPLICATION_JSON; 006import static com.box.sdk.http.ContentType.APPLICATION_JSON_PATCH; 007import static com.eclipsesource.json.Json.NULL; 008 009import com.box.sdk.http.HttpMethod; 010import com.box.sdk.internal.utils.Parsers; 011import com.box.sdk.sharedlink.BoxSharedLinkRequest; 012import com.eclipsesource.json.Json; 013import com.eclipsesource.json.JsonArray; 014import com.eclipsesource.json.JsonObject; 015import com.eclipsesource.json.JsonValue; 016import java.io.IOException; 017import java.io.InputStream; 018import java.io.OutputStream; 019import java.net.MalformedURLException; 020import java.net.URL; 021import java.util.ArrayList; 022import java.util.Arrays; 023import java.util.Collection; 024import java.util.Date; 025import java.util.EnumSet; 026import java.util.HashSet; 027import java.util.List; 028import java.util.Map; 029import java.util.Optional; 030import java.util.Set; 031import java.util.concurrent.TimeUnit; 032 033 034/** 035 * Represents an individual file on Box. This class can be used to download a file's contents, upload new versions, and 036 * perform other common file operations (move, copy, delete, etc.). 037 * 038 * <p>Unless otherwise noted, the methods in this class can throw an unchecked {@link BoxAPIException} (unchecked 039 * meaning that the compiler won't force you to handle it) if an error occurs. If you wish to implement custom error 040 * handling for errors related to the Box REST API, you should capture this exception explicitly. 041 */ 042@BoxResourceType("file") 043public class BoxFile extends BoxItem { 044 045 /** 046 * An array of all possible file fields that can be requested when calling {@link #getInfo(String...)}. 047 */ 048 public static final String[] ALL_FIELDS = {"type", "id", "sequence_id", "etag", "sha1", "name", 049 "description", "size", "path_collection", "created_at", "modified_at", 050 "trashed_at", "purged_at", "content_created_at", "content_modified_at", 051 "created_by", "modified_by", "owned_by", "shared_link", "parent", 052 "item_status", "version_number", "comment_count", "permissions", "tags", 053 "lock", "extension", "is_package", "file_version", "collections", 054 "watermark_info", "metadata", "representations", 055 "is_external_only", "expiring_embed_link", "allowed_invitee_roles", 056 "has_collaborations", "disposition_at", "is_accessible_via_shared_link"}; 057 058 /** 059 * An array of all possible version fields that can be requested when calling {@link #getVersions(String...)}. 060 */ 061 public static final String[] ALL_VERSION_FIELDS = {"id", "sha1", "name", "size", "uploader_display_name", 062 "created_at", "modified_at", "modified_by", "trashed_at", "trashed_by", "restored_at", "restored_by", 063 "purged_at", "file_version", "version_number"}; 064 /** 065 * File URL Template. 066 */ 067 public static final URLTemplate FILE_URL_TEMPLATE = new URLTemplate("files/%s"); 068 /** 069 * Content URL Template. 070 */ 071 public static final URLTemplate CONTENT_URL_TEMPLATE = new URLTemplate("files/%s/content"); 072 /** 073 * Versions URL Template. 074 */ 075 public static final URLTemplate VERSIONS_URL_TEMPLATE = new URLTemplate("files/%s/versions"); 076 /** 077 * Copy URL Template. 078 */ 079 public static final URLTemplate COPY_URL_TEMPLATE = new URLTemplate("files/%s/copy"); 080 /** 081 * Add Comment URL Template. 082 */ 083 public static final URLTemplate ADD_COMMENT_URL_TEMPLATE = new URLTemplate("comments"); 084 /** 085 * Get Comments URL Template. 086 */ 087 public static final URLTemplate GET_COMMENTS_URL_TEMPLATE = new URLTemplate("files/%s/comments"); 088 /** 089 * Metadata URL Template. 090 */ 091 public static final URLTemplate METADATA_URL_TEMPLATE = new URLTemplate("files/%s/metadata/%s/%s"); 092 /** 093 * Add Task URL Template. 094 */ 095 public static final URLTemplate ADD_TASK_URL_TEMPLATE = new URLTemplate("tasks"); 096 /** 097 * Get Tasks URL Template. 098 */ 099 public static final URLTemplate GET_TASKS_URL_TEMPLATE = new URLTemplate("files/%s/tasks"); 100 /** 101 * Get Thumbnail PNG Template. 102 */ 103 public static final URLTemplate GET_THUMBNAIL_PNG_TEMPLATE = new URLTemplate("files/%s/thumbnail.png"); 104 /** 105 * Get Thumbnail JPG Template. 106 */ 107 public static final URLTemplate GET_THUMBNAIL_JPG_TEMPLATE = new URLTemplate("files/%s/thumbnail.jpg"); 108 /** 109 * Upload Session URL Template. 110 */ 111 public static final URLTemplate UPLOAD_SESSION_URL_TEMPLATE = new URLTemplate("files/%s/upload_sessions"); 112 /** 113 * Upload Session Status URL Template. 114 */ 115 public static final URLTemplate UPLOAD_SESSION_STATUS_URL_TEMPLATE = new URLTemplate( 116 "files/upload_sessions/%s/status"); 117 /** 118 * Abort Upload Session URL Template. 119 */ 120 public static final URLTemplate ABORT_UPLOAD_SESSION_URL_TEMPLATE = new URLTemplate("files/upload_sessions/%s"); 121 /** 122 * Add Collaborations URL Template. 123 */ 124 public static final URLTemplate ADD_COLLABORATION_URL = new URLTemplate("collaborations"); 125 /** 126 * Get All File Collaborations URL Template. 127 */ 128 public static final URLTemplate GET_ALL_FILE_COLLABORATIONS_URL = new URLTemplate("files/%s/collaborations"); 129 /** 130 * Describes file item type. 131 */ 132 static final String TYPE = "file"; 133 private static final int GET_COLLABORATORS_PAGE_SIZE = 1000; 134 135 /** 136 * Constructs a BoxFile for a file with a given ID. 137 * 138 * @param api the API connection to be used by the file. 139 * @param id the ID of the file. 140 */ 141 public BoxFile(BoxAPIConnection api, String id) { 142 super(api, id); 143 } 144 145 /** 146 * {@inheritDoc} 147 */ 148 @Override 149 protected URL getItemURL() { 150 return FILE_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 151 } 152 153 /** 154 * Creates a shared link. 155 * 156 * @param sharedLinkRequest Shared link to create 157 * @return Created shared link. 158 */ 159 public BoxSharedLink createSharedLink(BoxSharedLinkRequest sharedLinkRequest) { 160 return createSharedLink(sharedLinkRequest.asSharedLink()); 161 } 162 163 private BoxSharedLink createSharedLink(BoxSharedLink sharedLink) { 164 Info info = new Info(); 165 info.setSharedLink(sharedLink); 166 167 this.updateInfo(info); 168 return info.getSharedLink(); 169 } 170 171 /** 172 * Adds new {@link BoxWebHook} to this {@link BoxFile}. 173 * 174 * @param address {@link BoxWebHook.Info#getAddress()} 175 * @param triggers {@link BoxWebHook.Info#getTriggers()} 176 * @return created {@link BoxWebHook.Info} 177 */ 178 public BoxWebHook.Info addWebHook(URL address, BoxWebHook.Trigger... triggers) { 179 return BoxWebHook.create(this, address, triggers); 180 } 181 182 /** 183 * Adds a comment to this file. The message can contain @mentions by using the string @[userid:username] anywhere 184 * within the message, where userid and username are the ID and username of the person being mentioned. 185 * 186 * @param message the comment's message. 187 * @return information about the newly added comment. 188 * @see <a href="https://developers.box.com/docs/#comments-add-a-comment-to-an-item">the tagged_message field 189 * for including @mentions.</a> 190 */ 191 public BoxComment.Info addComment(String message) { 192 JsonObject itemJSON = new JsonObject(); 193 itemJSON.add("type", "file"); 194 itemJSON.add("id", this.getID()); 195 196 JsonObject requestJSON = new JsonObject(); 197 requestJSON.add("item", itemJSON); 198 if (BoxComment.messageContainsMention(message)) { 199 requestJSON.add("tagged_message", message); 200 } else { 201 requestJSON.add("message", message); 202 } 203 204 URL url = ADD_COMMENT_URL_TEMPLATE.build(this.getAPI().getBaseURL()); 205 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "POST"); 206 request.setBody(requestJSON.toString()); 207 try (BoxJSONResponse response = request.send()) { 208 JsonObject responseJSON = Json.parse(response.getJSON()).asObject(); 209 210 BoxComment addedComment = new BoxComment(this.getAPI(), responseJSON.get("id").asString()); 211 return addedComment.new Info(responseJSON); 212 } 213 } 214 215 /** 216 * Adds a new task to this file. The task can have an optional message to include, and a due date. 217 * 218 * @param action the action the task assignee will be prompted to do. 219 * @param message an optional message to include with the task. 220 * @param dueAt the day at which this task is due. 221 * @return information about the newly added task. 222 */ 223 public BoxTask.Info addTask(BoxTask.Action action, String message, Date dueAt) { 224 return this.addTask(action, message, dueAt, null); 225 } 226 227 /** 228 * Adds a new task to this file. The task can have an optional message to include, due date, 229 * and task completion rule. 230 * 231 * @param action the action the task assignee will be prompted to do. 232 * @param message an optional message to include with the task. 233 * @param dueAt the day at which this task is due. 234 * @param completionRule the rule for completing the task. 235 * @return information about the newly added task. 236 */ 237 public BoxTask.Info addTask(BoxTask.Action action, String message, Date dueAt, 238 BoxTask.CompletionRule completionRule) { 239 JsonObject itemJSON = new JsonObject(); 240 itemJSON.add("type", "file"); 241 itemJSON.add("id", this.getID()); 242 243 JsonObject requestJSON = new JsonObject(); 244 requestJSON.add("item", itemJSON); 245 requestJSON.add("action", action.toJSONString()); 246 247 if (message != null && !message.isEmpty()) { 248 requestJSON.add("message", message); 249 } 250 251 if (dueAt != null) { 252 requestJSON.add("due_at", BoxDateFormat.format(dueAt)); 253 } 254 255 if (completionRule != null) { 256 requestJSON.add("completion_rule", completionRule.toJSONString()); 257 } 258 259 URL url = ADD_TASK_URL_TEMPLATE.build(this.getAPI().getBaseURL()); 260 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "POST"); 261 request.setBody(requestJSON.toString()); 262 try (BoxJSONResponse response = request.send()) { 263 JsonObject responseJSON = Json.parse(response.getJSON()).asObject(); 264 265 BoxTask addedTask = new BoxTask(this.getAPI(), responseJSON.get("id").asString()); 266 return addedTask.new Info(responseJSON); 267 } 268 } 269 270 /** 271 * Gets an expiring URL for downloading a file directly from Box. This can be user, 272 * for example, for sending as a redirect to a browser to cause the browser 273 * to download the file directly from Box. 274 * 275 * @return the temporary download URL 276 */ 277 public URL getDownloadURL() { 278 URL url = getDownloadUrl(); 279 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 280 request.setFollowRedirects(false); 281 282 try (BoxAPIResponse response = request.send()) { 283 String location = response.getHeaderField("location"); 284 285 try { 286 return new URL(location); 287 } catch (MalformedURLException e) { 288 throw new RuntimeException(e); 289 } 290 } 291 } 292 293 /** 294 * Downloads the contents of this file to a given OutputStream. 295 * 296 * @param output the stream to where the file will be written. 297 */ 298 public void download(OutputStream output) { 299 this.download(output, null); 300 } 301 302 /** 303 * Downloads the contents of this file to a given OutputStream while reporting the progress to a ProgressListener. 304 * 305 * @param output the stream to where the file will be written. 306 * @param listener a listener for monitoring the download's progress. 307 */ 308 public void download(OutputStream output, ProgressListener listener) { 309 URL url = getDownloadUrl(); 310 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 311 BoxAPIResponse response = request.send(); 312 writeStream(response, output, listener); 313 } 314 315 /** 316 * Downloads a part of this file's contents, starting at specified byte offset. 317 * 318 * @param output the stream to where the file will be written. 319 * @param offset the byte offset at which to start the download. 320 */ 321 public void downloadRange(OutputStream output, long offset) { 322 this.downloadRange(output, offset, -1); 323 } 324 325 /** 326 * Downloads a part of this file's contents, starting at rangeStart and stopping at rangeEnd. 327 * 328 * @param output the stream to where the file will be written. 329 * @param rangeStart the byte offset at which to start the download. 330 * @param rangeEnd the byte offset at which to stop the download. 331 */ 332 public void downloadRange(OutputStream output, long rangeStart, long rangeEnd) { 333 this.downloadRange(output, rangeStart, rangeEnd, null); 334 } 335 336 /** 337 * Downloads a part of this file's contents, starting at rangeStart and stopping at rangeEnd, while reporting the 338 * progress to a ProgressListener. 339 * 340 * @param output the stream to where the file will be written. 341 * @param rangeStart the byte offset at which to start the download. 342 * @param rangeEnd the byte offset at which to stop the download. 343 * @param listener a listener for monitoring the download's progress. 344 */ 345 public void downloadRange(OutputStream output, long rangeStart, long rangeEnd, ProgressListener listener) { 346 URL url = getDownloadUrl(); 347 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 348 if (rangeEnd > 0) { 349 request.addHeader("Range", String.format("bytes=%s-%s", rangeStart, rangeEnd)); 350 } else { 351 request.addHeader("Range", String.format("bytes=%s-", rangeStart)); 352 } 353 writeStream(request.send(), output, listener); 354 } 355 356 /** 357 * Can be used to override the URL used for file download. 358 * 359 * @return URL for file downalod 360 */ 361 protected URL getDownloadUrl() { 362 return CONTENT_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 363 } 364 365 @Override 366 public BoxFile.Info copy(BoxFolder destination) { 367 return this.copy(destination, null); 368 } 369 370 @Override 371 public BoxFile.Info copy(BoxFolder destination, String newName) { 372 URL url = COPY_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 373 374 JsonObject parent = new JsonObject(); 375 parent.add("id", destination.getID()); 376 377 JsonObject copyInfo = new JsonObject(); 378 copyInfo.add("parent", parent); 379 if (newName != null) { 380 copyInfo.add("name", newName); 381 } 382 383 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "POST"); 384 request.setBody(copyInfo.toString()); 385 try (BoxJSONResponse response = request.send()) { 386 JsonObject responseJSON = Json.parse(response.getJSON()).asObject(); 387 BoxFile copiedFile = new BoxFile(this.getAPI(), responseJSON.get("id").asString()); 388 return copiedFile.new Info(responseJSON); 389 } 390 } 391 392 /** 393 * Deletes this file by moving it to the trash. 394 */ 395 public void delete() { 396 URL url = FILE_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 397 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "DELETE"); 398 request.send().close(); 399 } 400 401 @Override 402 public BoxItem.Info move(BoxFolder destination) { 403 return this.move(destination, null); 404 } 405 406 @Override 407 public BoxItem.Info move(BoxFolder destination, String newName) { 408 URL url = FILE_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 409 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT"); 410 411 JsonObject parent = new JsonObject(); 412 parent.add("id", destination.getID()); 413 414 JsonObject updateInfo = new JsonObject(); 415 updateInfo.add("parent", parent); 416 if (newName != null) { 417 updateInfo.add("name", newName); 418 } 419 420 request.setBody(updateInfo.toString()); 421 try (BoxJSONResponse response = request.send()) { 422 JsonObject responseJSON = Json.parse(response.getJSON()).asObject(); 423 BoxFile movedFile = new BoxFile(this.getAPI(), responseJSON.get("id").asString()); 424 return movedFile.new Info(responseJSON); 425 } 426 } 427 428 /** 429 * Renames this file. 430 * 431 * @param newName the new name of the file. 432 */ 433 public void rename(String newName) { 434 URL url = FILE_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 435 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT"); 436 437 JsonObject updateInfo = new JsonObject(); 438 updateInfo.add("name", newName); 439 440 request.setBody(updateInfo.toString()); 441 try (BoxJSONResponse response = request.send()) { 442 response.getJSON(); 443 } 444 } 445 446 @Override 447 public BoxFile.Info getInfo(String... fields) { 448 URL url = FILE_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 449 if (fields.length > 0) { 450 String queryString = new QueryStringBuilder().appendParam("fields", fields).toString(); 451 url = FILE_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), queryString, this.getID()); 452 } 453 454 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "GET"); 455 try (BoxJSONResponse response = request.send()) { 456 return new Info(response.getJSON()); 457 } 458 } 459 460 /** 461 * Gets information about this item including a specified set of representations. 462 * 463 * @param representationHints hints for representations to be retrieved 464 * @param fields the fields to retrieve. 465 * @return info about this item containing only the specified fields, including representations. 466 * @see <a href=https://developer.box.com/reference#section-x-rep-hints-header>X-Rep-Hints Header</a> 467 */ 468 public BoxFile.Info getInfoWithRepresentations(String representationHints, String... fields) { 469 if (representationHints.matches(Representation.X_REP_HINTS_PATTERN)) { 470 //Since the user intends to get representations, add it to fields, even if user has missed it 471 Set<String> fieldsSet = new HashSet<>(Arrays.asList(fields)); 472 fieldsSet.add("representations"); 473 String queryString = new QueryStringBuilder().appendParam("fields", 474 fieldsSet.toArray(new String[0])).toString(); 475 URL url = FILE_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), queryString, this.getID()); 476 477 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "GET"); 478 request.addHeader("X-Rep-Hints", representationHints); 479 try (BoxJSONResponse response = request.send()) { 480 return new Info(response.getJSON()); 481 } 482 } else { 483 throw new BoxAPIException( 484 "Represention hints is not valid. Refer documention on how to construct X-Rep-Hints Header" 485 ); 486 } 487 } 488 489 /** 490 * Fetches the contents of a file representation and writes them to the provided output stream. 491 * 492 * @param representationHint the X-Rep-Hints query for the representation to fetch. 493 * @param output the output stream to write the contents to. 494 * @see <a href=https://developer.box.com/reference#section-x-rep-hints-header>X-Rep-Hints Header</a> 495 */ 496 public void getRepresentationContent(String representationHint, OutputStream output) { 497 498 this.getRepresentationContent(representationHint, "", output); 499 } 500 501 /** 502 * Fetches the contents of a file representation with asset path and writes them to the provided output stream. 503 * 504 * @param representationHint the X-Rep-Hints query for the representation to fetch. 505 * @param assetPath the path of the asset for representations containing multiple files. 506 * @param output the output stream to write the contents to. 507 * @see <a href=https://developer.box.com/reference#section-x-rep-hints-header>X-Rep-Hints Header</a> 508 */ 509 public void getRepresentationContent(String representationHint, String assetPath, OutputStream output) { 510 this.getRepresentationContent(representationHint, assetPath, output, Integer.MAX_VALUE); 511 } 512 513 /** 514 * Fetches the contents of a file representation with asset path and writes them to the provided output stream. 515 * 516 * @param representationHint the X-Rep-Hints query for the representation to fetch. 517 * @param assetPath the path of the asset for representations containing multiple files. 518 * @param output the output stream to write the contents to. 519 * @param maxRetries the maximum number of attempts to call the request for retrieving status information 520 * indicating whether the representation has been generated and is ready to fetch. 521 * If the number of attempts is exceeded, the method will throw a BoxApiException. 522 * @see <a href=https://developer.box.com/reference#section-x-rep-hints-header>X-Rep-Hints Header</a> 523 */ 524 public void getRepresentationContent( 525 String representationHint, String assetPath, OutputStream output, int maxRetries 526 ) { 527 List<Representation> reps = this.getInfoWithRepresentations(representationHint).getRepresentations(); 528 if (reps.size() < 1) { 529 throw new BoxAPIException("No matching representations found for requested '" + representationHint 530 + "' hint"); 531 } 532 Representation representation = reps.get(0); 533 String repState = representation.getStatus().getState(); 534 535 switch (repState) { 536 case "viewable": 537 case "success": 538 this.makeRepresentationContentRequest(representation.getContent().getUrlTemplate(), assetPath, output); 539 break; 540 case "pending": 541 case "none": 542 543 String repContentURLString = null; 544 int attemptNumber = 0; 545 while (repContentURLString == null && attemptNumber < maxRetries) { 546 repContentURLString = this.pollRepInfo(representation.getInfo().getUrl()); 547 try { 548 Thread.sleep(100); 549 } catch (InterruptedException e) { 550 throw new RuntimeException(e); 551 } 552 attemptNumber++; 553 } 554 555 if (repContentURLString != null) { 556 this.makeRepresentationContentRequest(repContentURLString, assetPath, output); 557 } else { 558 throw new BoxAPIException( 559 "Representation did not have a success status allowing it to be retrieved after " 560 + maxRetries 561 + " attempts" 562 ); 563 } 564 565 break; 566 case "error": 567 throw new BoxAPIException("Representation had error status"); 568 default: 569 throw new BoxAPIException("Representation had unknown status"); 570 } 571 572 } 573 574 private String pollRepInfo(URL infoURL) { 575 576 BoxJSONRequest infoRequest = new BoxJSONRequest(this.getAPI(), infoURL, HttpMethod.GET); 577 try (BoxJSONResponse infoResponse = infoRequest.send()) { 578 JsonObject response = infoResponse.getJsonObject(); 579 580 Representation rep = new Representation(response); 581 582 String repState = rep.getStatus().getState(); 583 584 switch (repState) { 585 case "viewable": 586 case "success": 587 return rep.getContent().getUrlTemplate(); 588 case "pending": 589 case "none": 590 return null; 591 case "error": 592 throw new BoxAPIException("Representation had error status"); 593 default: 594 throw new BoxAPIException("Representation had unknown status"); 595 } 596 } 597 } 598 599 private void makeRepresentationContentRequest( 600 String representationURLTemplate, String assetPath, OutputStream output 601 ) { 602 try { 603 URL repURL = new URL(representationURLTemplate.replace("{+asset_path}", assetPath)); 604 BoxAPIRequest repContentReq = new BoxAPIRequest(this.getAPI(), repURL, HttpMethod.GET); 605 BoxAPIResponse response = repContentReq.send(); 606 writeStreamWithContentLength(response, output); 607 } catch (MalformedURLException ex) { 608 609 throw new BoxAPIException("Could not generate representation content URL"); 610 } 611 } 612 613 /** 614 * Updates the information about this file with any info fields that have been modified locally. 615 * 616 * <p>The only fields that will be updated are the ones that have been modified locally. For example, the following 617 * code won't update any information (or even send a network request) since none of the info's fields were 618 * changed:</p> 619 * 620 * <pre>BoxFile file = new File(api, id); 621 * BoxFile.Info info = file.getInfo(); 622 * file.updateInfo(info);</pre> 623 * 624 * @param info the updated info. 625 */ 626 public void updateInfo(BoxFile.Info info) { 627 URL url = FILE_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 628 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT"); 629 request.setBody(info.getPendingChanges()); 630 try (BoxJSONResponse response = request.send()) { 631 JsonObject jsonObject = Json.parse(response.getJSON()).asObject(); 632 info.update(jsonObject); 633 } 634 } 635 636 /** 637 * Retrieve a specific file version. 638 * 639 * @param fileVersionID the ID of the file version to retrieve. 640 * @param fields the optional fields to retrieve. 641 * @return a specific file version. 642 */ 643 public BoxFileVersion getVersionByID(String fileVersionID, String... fields) { 644 URL url = BoxFileVersion.VERSION_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), fileVersionID); 645 if (fields.length > 0) { 646 String queryString = new QueryStringBuilder().appendParam("fields", fields).toString(); 647 url = BoxFileVersion.VERSION_URL_TEMPLATE.buildWithQuery( 648 this.getAPI().getBaseURL(), 649 queryString, 650 this.getID(), 651 fileVersionID 652 ); 653 } 654 655 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "GET"); 656 try (BoxJSONResponse response = request.send()) { 657 JsonObject jsonObject = Json.parse(response.getJSON()).asObject(); 658 return new BoxFileVersion(this.getAPI(), jsonObject, this.getID()); 659 } 660 } 661 662 /** 663 * Gets up to 1000 versions of this file. Note that only users with premium accounts will be able to retrieve 664 * previous versions of their files. `fields` parameter is optional, if specified only requested fields will 665 * be returned: 666 * <pre> 667 * {@code 668 * new BoxFile(api, file_id).getVersions() // will return all default fields 669 * new BoxFile(api, file_id).getVersions("name") // will return only specified fields 670 * } 671 * </pre> 672 * 673 * @param fields the fields to retrieve. If nothing provided default fields will be returned. 674 * You can find list of available fields at {@link BoxFile#ALL_VERSION_FIELDS} 675 * @return a list of previous file versions. 676 */ 677 public Collection<BoxFileVersion> getVersions(String... fields) { 678 return getVersionsRange(0, BoxFileVersion.DEFAULT_LIMIT, fields); 679 } 680 681 682 /** 683 * Retrieves a specific range of versions of this file. 684 * 685 * @param offset the index of the first version of this file to retrieve. 686 * @param limit the maximum number of versions to retrieve after the offset. 687 * @param fields the fields to retrieve. 688 * @return a partial collection containing the specified range of versions of this file. 689 */ 690 public PartialCollection<BoxFileVersion> getVersionsRange(long offset, long limit, String... fields) { 691 QueryStringBuilder builder = new QueryStringBuilder() 692 .appendParam("limit", limit) 693 .appendParam("offset", offset); 694 695 if (fields.length > 0) { 696 builder.appendParam("fields", fields); 697 } 698 699 URL url = VERSIONS_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), builder.toString(), this.getID()); 700 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "GET"); 701 try (BoxJSONResponse response = request.send()) { 702 703 JsonObject jsonObject = Json.parse(response.getJSON()).asObject(); 704 String totalCountString = jsonObject.get("total_count").toString(); 705 long fullSize = Double.valueOf(totalCountString).longValue(); 706 PartialCollection<BoxFileVersion> versions = new PartialCollection<>(offset, limit, fullSize); 707 JsonArray entries = jsonObject.get("entries").asArray(); 708 for (JsonValue entry : entries) { 709 versions.add(new BoxFileVersion(this.getAPI(), entry.asObject(), this.getID())); 710 } 711 712 return versions; 713 } 714 } 715 716 /** 717 * Checks if a new version of the file can be uploaded with the specified name. 718 * 719 * @param name the new name for the file. 720 * @return whether or not the file version can be uploaded. 721 */ 722 public boolean canUploadVersion(String name) { 723 return this.canUploadVersion(name, 0); 724 } 725 726 /** 727 * Checks if a new version of the file can be uploaded with the specified name and size. 728 * 729 * @param name the new name for the file. 730 * @param fileSize the size of the new version content in bytes. 731 * @return whether the file version can be uploaded. 732 */ 733 public boolean canUploadVersion(String name, long fileSize) { 734 735 URL url = getDownloadUrl(); 736 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "OPTIONS"); 737 738 JsonObject preflightInfo = new JsonObject(); 739 if (name != null) { 740 preflightInfo.add("name", name); 741 } 742 743 preflightInfo.add("size", fileSize); 744 745 request.setBody(preflightInfo.toString()); 746 try (BoxAPIResponse response = request.send()) { 747 return response.getResponseCode() == 200; 748 } catch (BoxAPIException ex) { 749 if (ex.getResponseCode() >= 400 && ex.getResponseCode() < 500) { 750 // This looks like an error response, meaning the upload would fail 751 return false; 752 } else { 753 // This looks like a network error or server error, rethrow exception 754 throw ex; 755 } 756 } 757 } 758 759 /** 760 * Uploads a new version of this file, replacing the current version. Note that only users with premium accounts 761 * will be able to view and recover previous versions of the file. 762 * 763 * @param fileContent a stream containing the new file contents. 764 * @return the uploaded file version. 765 */ 766 public BoxFile.Info uploadNewVersion(InputStream fileContent) { 767 return this.uploadNewVersion(fileContent, null); 768 } 769 770 /** 771 * Uploads a new version of this file, replacing the current version. Note that only users with premium accounts 772 * will be able to view and recover previous versions of the file. 773 * 774 * @param fileContent a stream containing the new file contents. 775 * @param fileContentSHA1 a string containing the SHA1 hash of the new file contents. 776 * @return the uploaded file version. 777 */ 778 public BoxFile.Info uploadNewVersion(InputStream fileContent, String fileContentSHA1) { 779 return this.uploadNewVersion(fileContent, fileContentSHA1, null); 780 } 781 782 /** 783 * Uploads a new version of this file, replacing the current version. Note that only users with premium accounts 784 * will be able to view and recover previous versions of the file. 785 * 786 * @param fileContent a stream containing the new file contents. 787 * @param fileContentSHA1 a string containing the SHA1 hash of the new file contents. 788 * @param modified the date that the new version was modified. 789 * @return the uploaded file version. 790 */ 791 public BoxFile.Info uploadNewVersion(InputStream fileContent, String fileContentSHA1, Date modified) { 792 return this.uploadNewVersion(fileContent, fileContentSHA1, modified, 0, null); 793 } 794 795 /** 796 * Uploads a new version of this file, replacing the current version. Note that only users with premium accounts 797 * will be able to view and recover previous versions of the file. 798 * 799 * @param fileContent a stream containing the new file contents. 800 * @param fileContentSHA1 a string containing the SHA1 hash of the new file contents. 801 * @param modified the date that the new version was modified. 802 * @param name the new name for the file 803 * @return the uploaded file version. 804 */ 805 public BoxFile.Info uploadNewVersion(InputStream fileContent, String fileContentSHA1, Date modified, String name) { 806 return this.uploadNewVersion(fileContent, fileContentSHA1, modified, name, 0, null); 807 } 808 809 /** 810 * Uploads a new version of this file, replacing the current version, while reporting the progress to a 811 * ProgressListener. Note that only users with premium accounts will be able to view and recover previous versions 812 * of the file. 813 * 814 * @param fileContent a stream containing the new file contents. 815 * @param modified the date that the new version was modified. 816 * @param fileSize the size of the file used for determining the progress of the upload. 817 * @param listener a listener for monitoring the upload's progress. 818 * @return the uploaded file version. 819 */ 820 public BoxFile.Info uploadNewVersion(InputStream fileContent, Date modified, long fileSize, 821 ProgressListener listener) { 822 return this.uploadNewVersion(fileContent, null, modified, fileSize, listener); 823 } 824 825 /** 826 * Uploads a new version of this file, replacing the current version, while reporting the progress to a 827 * ProgressListener. Note that only users with premium accounts will be able to view and recover previous versions 828 * of the file. 829 * 830 * @param fileContent a stream containing the new file contents. 831 * @param fileContentSHA1 the SHA1 hash of the file contents. will be sent along in the Content-MD5 header 832 * @param modified the date that the new version was modified. 833 * @param fileSize the size of the file used for determining the progress of the upload. 834 * @param listener a listener for monitoring the upload's progress. 835 * @return the uploaded file version. 836 */ 837 public BoxFile.Info uploadNewVersion(InputStream fileContent, String fileContentSHA1, Date modified, long fileSize, 838 ProgressListener listener) { 839 return this.uploadNewVersion(fileContent, fileContentSHA1, modified, null, fileSize, listener); 840 } 841 842 /** 843 * Uploads a new version of this file, replacing the current version, while reporting the progress to a 844 * ProgressListener. Note that only users with premium accounts will be able to view and recover previous versions 845 * of the file. 846 * 847 * @param fileContent a stream containing the new file contents. 848 * @param fileContentSHA1 the SHA1 hash of the file contents. will be sent along in the Content-MD5 header 849 * @param modified the date that the new version was modified. 850 * @param name the new name for the file 851 * @param fileSize the size of the file used for determining the progress of the upload. 852 * @param listener a listener for monitoring the upload's progress. 853 * @return the uploaded file version. 854 */ 855 public BoxFile.Info uploadNewVersion(InputStream fileContent, String fileContentSHA1, Date modified, String name, 856 long fileSize, ProgressListener listener) { 857 URL uploadURL = CONTENT_URL_TEMPLATE.build(this.getAPI().getBaseUploadURL(), this.getID()); 858 BoxMultipartRequest request = new BoxMultipartRequest(getAPI(), uploadURL); 859 860 if (fileSize > 0) { 861 request.setFile(fileContent, "", fileSize); 862 } else { 863 request.setFile(fileContent, ""); 864 } 865 866 if (fileContentSHA1 != null) { 867 request.setContentSHA1(fileContentSHA1); 868 } 869 870 JsonObject attributesJSON = new JsonObject(); 871 if (modified != null) { 872 attributesJSON.add("content_modified_at", BoxDateFormat.format(modified)); 873 } 874 875 if (name != null) { 876 attributesJSON.add("name", name); 877 } 878 879 request.putField("attributes", attributesJSON.toString()); 880 881 BoxJSONResponse response = null; 882 try { 883 if (listener == null) { 884 // upload is multipart request but response is JSON 885 response = (BoxJSONResponse) request.send(); 886 } else { 887 // upload is multipart request but response is JSON 888 response = (BoxJSONResponse) request.send(listener); 889 } 890 891 String fileJSON = response.getJsonObject().get("entries").asArray().get(0).toString(); 892 893 return new BoxFile.Info(fileJSON); 894 } finally { 895 Optional.ofNullable(response).ifPresent(BoxAPIResponse::close); 896 } 897 } 898 899 /** 900 * Gets an expiring URL for creating an embedded preview session. The URL will expire after 60 seconds and the 901 * preview session will expire after 60 minutes. 902 * 903 * @return the expiring preview link 904 */ 905 public URL getPreviewLink() { 906 BoxFile.Info info = this.getInfo("expiring_embed_link"); 907 908 return info.getPreviewLink(); 909 } 910 911 /** 912 * Gets a list of any comments on this file. 913 * 914 * @return a list of comments on this file. 915 */ 916 public List<BoxComment.Info> getComments() { 917 URL url = GET_COMMENTS_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 918 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "GET"); 919 try (BoxJSONResponse response = request.send()) { 920 JsonObject responseJSON = Json.parse(response.getJSON()).asObject(); 921 922 int totalCount = responseJSON.get("total_count").asInt(); 923 List<BoxComment.Info> comments = new ArrayList<>(totalCount); 924 JsonArray entries = responseJSON.get("entries").asArray(); 925 for (JsonValue value : entries) { 926 JsonObject commentJSON = value.asObject(); 927 BoxComment comment = new BoxComment(this.getAPI(), commentJSON.get("id").asString()); 928 BoxComment.Info info = comment.new Info(commentJSON); 929 comments.add(info); 930 } 931 932 return comments; 933 } 934 } 935 936 /** 937 * Gets a list of any tasks on this file with requested fields. 938 * 939 * @param fields optional fields to retrieve for this task. 940 * @return a list of tasks on this file. 941 */ 942 public List<BoxTask.Info> getTasks(String... fields) { 943 QueryStringBuilder builder = new QueryStringBuilder(); 944 if (fields.length > 0) { 945 builder.appendParam("fields", fields); 946 } 947 URL url = GET_TASKS_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), builder.toString(), this.getID()); 948 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "GET"); 949 try (BoxJSONResponse response = request.send()) { 950 JsonObject responseJSON = Json.parse(response.getJSON()).asObject(); 951 952 int totalCount = responseJSON.get("total_count").asInt(); 953 List<BoxTask.Info> tasks = new ArrayList<>(totalCount); 954 JsonArray entries = responseJSON.get("entries").asArray(); 955 for (JsonValue value : entries) { 956 JsonObject taskJSON = value.asObject(); 957 BoxTask task = new BoxTask(this.getAPI(), taskJSON.get("id").asString()); 958 BoxTask.Info info = task.new Info(taskJSON); 959 tasks.add(info); 960 } 961 962 return tasks; 963 } 964 } 965 966 /** 967 * Creates metadata on this file in the global properties template. 968 * 969 * @param metadata The new metadata values. 970 * @return the metadata returned from the server. 971 */ 972 public Metadata createMetadata(Metadata metadata) { 973 return this.createMetadata(Metadata.DEFAULT_METADATA_TYPE, metadata); 974 } 975 976 /** 977 * Creates metadata on this file in the specified template type. 978 * 979 * @param typeName the metadata template type name. 980 * @param metadata the new metadata values. 981 * @return the metadata returned from the server. 982 */ 983 public Metadata createMetadata(String typeName, Metadata metadata) { 984 String scope = Metadata.scopeBasedOnType(typeName); 985 return this.createMetadata(typeName, scope, metadata); 986 } 987 988 /** 989 * Creates metadata on this file in the specified template type. 990 * 991 * @param typeName the metadata template type name. 992 * @param scope the metadata scope (global or enterprise). 993 * @param metadata the new metadata values. 994 * @return the metadata returned from the server. 995 */ 996 public Metadata createMetadata(String typeName, String scope, Metadata metadata) { 997 URL url = METADATA_URL_TEMPLATE.buildAlpha(this.getAPI().getBaseURL(), this.getID(), scope, typeName); 998 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "POST"); 999 request.setBody(metadata.toString()); 1000 try (BoxJSONResponse response = request.send()) { 1001 return new Metadata(Json.parse(response.getJSON()).asObject()); 1002 } 1003 } 1004 1005 /** 1006 * Sets the provided metadata on the file. If metadata has already been created on this file, 1007 * it overwrites metadata keys specified in the `metadata` param. 1008 * 1009 * @param templateName the name of the metadata template. 1010 * @param scope the scope of the template (usually "global" or "enterprise"). 1011 * @param metadata the new metadata values. 1012 * @return the metadata returned from the server. 1013 */ 1014 public Metadata setMetadata(String templateName, String scope, Metadata metadata) { 1015 try { 1016 return this.createMetadata(templateName, scope, metadata); 1017 } catch (BoxAPIException e) { 1018 if (e.getResponseCode() == 409) { 1019 if (metadata.getOperations().isEmpty()) { 1020 return getMetadata(); 1021 } else { 1022 return updateExistingTemplate(templateName, scope, metadata); 1023 } 1024 } else { 1025 throw e; 1026 } 1027 } 1028 } 1029 1030 private Metadata updateExistingTemplate(String templateName, String scope, Metadata metadata) { 1031 Metadata metadataToUpdate = new Metadata(scope, templateName); 1032 for (JsonValue value : metadata.getOperations()) { 1033 if (value.asObject().get("value").isNumber()) { 1034 metadataToUpdate.add(value.asObject().get("path").asString(), 1035 value.asObject().get("value").asDouble()); 1036 } else if (value.asObject().get("value").isString()) { 1037 metadataToUpdate.add(value.asObject().get("path").asString(), 1038 value.asObject().get("value").asString()); 1039 } else if (value.asObject().get("value").isArray()) { 1040 ArrayList<String> list = new ArrayList<>(); 1041 for (JsonValue jsonValue : value.asObject().get("value").asArray()) { 1042 list.add(jsonValue.asString()); 1043 } 1044 metadataToUpdate.add(value.asObject().get("path").asString(), list); 1045 } 1046 } 1047 return this.updateMetadata(metadataToUpdate); 1048 } 1049 1050 /** 1051 * Adds a metadata classification to the specified file. 1052 * 1053 * @param classificationType the metadata classification type. 1054 * @return the metadata classification type added to the file. 1055 */ 1056 public String addClassification(String classificationType) { 1057 Metadata metadata = new Metadata().add(Metadata.CLASSIFICATION_KEY, classificationType); 1058 Metadata classification = this.createMetadata(Metadata.CLASSIFICATION_TEMPLATE_KEY, 1059 "enterprise", metadata); 1060 1061 return classification.getString(Metadata.CLASSIFICATION_KEY); 1062 } 1063 1064 /** 1065 * Updates a metadata classification on the specified file. 1066 * 1067 * @param classificationType the metadata classification type. 1068 * @return the new metadata classification type updated on the file. 1069 */ 1070 public String updateClassification(String classificationType) { 1071 Metadata metadata = new Metadata("enterprise", Metadata.CLASSIFICATION_TEMPLATE_KEY); 1072 metadata.add("/Box__Security__Classification__Key", classificationType); 1073 Metadata classification = this.updateMetadata(metadata); 1074 1075 return classification.getString(Metadata.CLASSIFICATION_KEY); 1076 } 1077 1078 /** 1079 * Attempts to add classification to a file. If classification already exists then do update. 1080 * 1081 * @param classificationType the metadata classification type. 1082 * @return the metadata classification type on the file. 1083 */ 1084 public String setClassification(String classificationType) { 1085 Metadata metadata = new Metadata().add(Metadata.CLASSIFICATION_KEY, classificationType); 1086 Metadata classification; 1087 1088 try { 1089 classification = this.createMetadata(Metadata.CLASSIFICATION_TEMPLATE_KEY, "enterprise", metadata); 1090 } catch (BoxAPIException e) { 1091 if (e.getResponseCode() == 409) { 1092 metadata = new Metadata("enterprise", Metadata.CLASSIFICATION_TEMPLATE_KEY); 1093 metadata.replace(Metadata.CLASSIFICATION_KEY, classificationType); 1094 classification = this.updateMetadata(metadata); 1095 } else { 1096 throw e; 1097 } 1098 } 1099 1100 return classification.getString(Metadata.CLASSIFICATION_KEY); 1101 } 1102 1103 /** 1104 * Gets the classification type for the specified file. 1105 * 1106 * @return the metadata classification type on the file. 1107 */ 1108 public String getClassification() { 1109 Metadata metadata; 1110 try { 1111 metadata = this.getMetadata(Metadata.CLASSIFICATION_TEMPLATE_KEY); 1112 1113 } catch (BoxAPIException e) { 1114 JsonObject responseObject = Json.parse(e.getResponse()).asObject(); 1115 String code = responseObject.get("code").asString(); 1116 1117 if (e.getResponseCode() == 404 && code.equals("instance_not_found")) { 1118 return null; 1119 } else { 1120 throw e; 1121 } 1122 } 1123 1124 return metadata.getString(Metadata.CLASSIFICATION_KEY); 1125 } 1126 1127 /** 1128 * Deletes the classification on the file. 1129 */ 1130 public void deleteClassification() { 1131 this.deleteMetadata(Metadata.CLASSIFICATION_TEMPLATE_KEY, "enterprise"); 1132 } 1133 1134 /** 1135 * Locks a file. 1136 * 1137 * @return the lock returned from the server. 1138 */ 1139 public BoxLock lock() { 1140 return this.lock(null, false); 1141 } 1142 1143 /** 1144 * Locks a file. 1145 * 1146 * @param isDownloadPrevented is downloading of file prevented when locked. 1147 * @return the lock returned from the server. 1148 */ 1149 public BoxLock lock(boolean isDownloadPrevented) { 1150 return this.lock(null, isDownloadPrevented); 1151 } 1152 1153 /** 1154 * Locks a file. 1155 * 1156 * @param expiresAt expiration date of the lock. 1157 * @return the lock returned from the server. 1158 */ 1159 public BoxLock lock(Date expiresAt) { 1160 return this.lock(expiresAt, false); 1161 } 1162 1163 /** 1164 * Locks a file. 1165 * 1166 * @param expiresAt expiration date of the lock. 1167 * @param isDownloadPrevented is downloading of file prevented when locked. 1168 * @return the lock returned from the server. 1169 */ 1170 public BoxLock lock(Date expiresAt, boolean isDownloadPrevented) { 1171 String queryString = new QueryStringBuilder().appendParam("fields", "lock").toString(); 1172 URL url = FILE_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), queryString, this.getID()); 1173 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT"); 1174 1175 JsonObject lockConfig = new JsonObject(); 1176 lockConfig.add("type", "lock"); 1177 if (expiresAt != null) { 1178 lockConfig.add("expires_at", BoxDateFormat.format(expiresAt)); 1179 } 1180 lockConfig.add("is_download_prevented", isDownloadPrevented); 1181 1182 JsonObject requestJSON = new JsonObject(); 1183 requestJSON.add("lock", lockConfig); 1184 request.setBody(requestJSON.toString()); 1185 1186 try (BoxJSONResponse response = request.send()) { 1187 1188 JsonObject responseJSON = Json.parse(response.getJSON()).asObject(); 1189 JsonValue lockValue = responseJSON.get("lock"); 1190 JsonObject lockJSON = Json.parse(lockValue.toString()).asObject(); 1191 1192 return new BoxLock(lockJSON, this.getAPI()); 1193 } 1194 } 1195 1196 /** 1197 * Unlocks a file. 1198 */ 1199 public void unlock() { 1200 String queryString = new QueryStringBuilder().appendParam("fields", "lock").toString(); 1201 URL url = FILE_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), queryString, this.getID()); 1202 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "PUT"); 1203 1204 JsonObject lockObject = new JsonObject(); 1205 lockObject.add("lock", NULL); 1206 1207 request.setBody(lockObject.toString()); 1208 request.send().close(); 1209 } 1210 1211 /** 1212 * Used to retrieve all metadata associated with the file. 1213 * 1214 * @param fields the optional fields to retrieve. 1215 * @return An iterable of metadata instances associated with the file. 1216 */ 1217 public Iterable<Metadata> getAllMetadata(String... fields) { 1218 return Metadata.getAllMetadata(this, fields); 1219 } 1220 1221 /** 1222 * Gets the file properties metadata. 1223 * 1224 * @return the metadata returned from the server. 1225 */ 1226 public Metadata getMetadata() { 1227 return this.getMetadata(Metadata.DEFAULT_METADATA_TYPE); 1228 } 1229 1230 /** 1231 * Gets the file metadata of specified template type. 1232 * 1233 * @param typeName the metadata template type name. 1234 * @return the metadata returned from the server. 1235 */ 1236 public Metadata getMetadata(String typeName) { 1237 String scope = Metadata.scopeBasedOnType(typeName); 1238 return this.getMetadata(typeName, scope); 1239 } 1240 1241 /** 1242 * Gets the file metadata of specified template type. 1243 * 1244 * @param typeName the metadata template type name. 1245 * @param scope the metadata scope (global or enterprise). 1246 * @return the metadata returned from the server. 1247 */ 1248 public Metadata getMetadata(String typeName, String scope) { 1249 URL url = METADATA_URL_TEMPLATE.buildAlpha(this.getAPI().getBaseURL(), this.getID(), scope, typeName); 1250 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "GET"); 1251 try (BoxJSONResponse response = request.send()) { 1252 return new Metadata(Json.parse(response.getJSON()).asObject()); 1253 } 1254 } 1255 1256 /** 1257 * Updates the file metadata. 1258 * 1259 * @param metadata the new metadata values. 1260 * @return the metadata returned from the server. 1261 */ 1262 public Metadata updateMetadata(Metadata metadata) { 1263 String scope; 1264 if (metadata.getScope().equals(Metadata.GLOBAL_METADATA_SCOPE)) { 1265 scope = Metadata.GLOBAL_METADATA_SCOPE; 1266 } else if (metadata.getScope().startsWith(Metadata.ENTERPRISE_METADATA_SCOPE)) { 1267 scope = metadata.getScope(); 1268 } else { 1269 scope = Metadata.ENTERPRISE_METADATA_SCOPE; 1270 } 1271 1272 URL url = METADATA_URL_TEMPLATE.buildAlpha(this.getAPI().getBaseURL(), this.getID(), 1273 scope, metadata.getTemplateName()); 1274 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT", APPLICATION_JSON_PATCH); 1275 request.setBody(metadata.getPatch()); 1276 try (BoxJSONResponse response = request.send()) { 1277 return new Metadata(Json.parse(response.getJSON()).asObject()); 1278 } 1279 } 1280 1281 /** 1282 * Deletes the file properties metadata. 1283 */ 1284 public void deleteMetadata() { 1285 this.deleteMetadata(Metadata.DEFAULT_METADATA_TYPE); 1286 } 1287 1288 /** 1289 * Deletes the file metadata of specified template type. 1290 * 1291 * @param typeName the metadata template type name. 1292 */ 1293 public void deleteMetadata(String typeName) { 1294 String scope = Metadata.scopeBasedOnType(typeName); 1295 this.deleteMetadata(typeName, scope); 1296 } 1297 1298 /** 1299 * Deletes the file metadata of specified template type. 1300 * 1301 * @param typeName the metadata template type name. 1302 * @param scope the metadata scope (global or enterprise). 1303 */ 1304 public void deleteMetadata(String typeName, String scope) { 1305 URL url = METADATA_URL_TEMPLATE.buildAlpha(this.getAPI().getBaseURL(), this.getID(), scope, typeName); 1306 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "DELETE"); 1307 request.send().close(); 1308 } 1309 1310 /** 1311 * Used to retrieve the watermark for the file. 1312 * If the file does not have a watermark applied to it, a 404 Not Found will be returned by API. 1313 * 1314 * @param fields the fields to retrieve. 1315 * @return the watermark associated with the file. 1316 */ 1317 public BoxWatermark getWatermark(String... fields) { 1318 return this.getWatermark(FILE_URL_TEMPLATE, fields); 1319 } 1320 1321 /** 1322 * Used to apply or update the watermark for the file. 1323 * 1324 * @return the watermark associated with the file. 1325 */ 1326 public BoxWatermark applyWatermark() { 1327 return this.applyWatermark(FILE_URL_TEMPLATE, BoxWatermark.WATERMARK_DEFAULT_IMPRINT); 1328 } 1329 1330 /** 1331 * Removes a watermark from the file. 1332 * If the file did not have a watermark applied to it, a 404 Not Found will be returned by API. 1333 */ 1334 public void removeWatermark() { 1335 this.removeWatermark(FILE_URL_TEMPLATE); 1336 } 1337 1338 /** 1339 * {@inheritDoc} 1340 */ 1341 @Override 1342 public BoxFile.Info setCollections(BoxCollection... collections) { 1343 JsonArray jsonArray = new JsonArray(); 1344 for (BoxCollection collection : collections) { 1345 JsonObject collectionJSON = new JsonObject(); 1346 collectionJSON.add("id", collection.getID()); 1347 jsonArray.add(collectionJSON); 1348 } 1349 JsonObject infoJSON = new JsonObject(); 1350 infoJSON.add("collections", jsonArray); 1351 1352 String queryString = new QueryStringBuilder().appendParam("fields", ALL_FIELDS).toString(); 1353 URL url = FILE_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), queryString, this.getID()); 1354 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT"); 1355 request.setBody(infoJSON.toString()); 1356 try (BoxJSONResponse response = request.send()) { 1357 JsonObject jsonObject = Json.parse(response.getJSON()).asObject(); 1358 return new Info(jsonObject); 1359 } 1360 } 1361 1362 /** 1363 * Creates an upload session to create a new version of a file in chunks. 1364 * This will first verify that the version can be created and then open a session for uploading pieces of the file. 1365 * 1366 * @param fileSize the size of the file that will be uploaded. 1367 * @return the created upload session instance. 1368 */ 1369 public BoxFileUploadSession.Info createUploadSession(long fileSize) { 1370 URL url = UPLOAD_SESSION_URL_TEMPLATE.build(this.getAPI().getBaseUploadURL(), this.getID()); 1371 1372 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "POST"); 1373 request.addHeader("Content-Type", APPLICATION_JSON); 1374 1375 JsonObject body = new JsonObject(); 1376 body.add("file_size", fileSize); 1377 request.setBody(body.toString()); 1378 1379 try (BoxJSONResponse response = request.send()) { 1380 JsonObject jsonObject = Json.parse(response.getJSON()).asObject(); 1381 1382 String sessionId = jsonObject.get("id").asString(); 1383 BoxFileUploadSession session = new BoxFileUploadSession(this.getAPI(), sessionId); 1384 return session.new Info(jsonObject); 1385 } 1386 } 1387 1388 /** 1389 * Creates a new version of a file. 1390 * 1391 * @param inputStream the stream instance that contains the data. 1392 * @param fileSize the size of the file that will be uploaded. 1393 * @return the created file instance. 1394 * @throws InterruptedException when a thread execution is interrupted. 1395 * @throws IOException when reading a stream throws exception. 1396 */ 1397 public BoxFile.Info uploadLargeFile(InputStream inputStream, long fileSize) 1398 throws InterruptedException, IOException { 1399 URL url = UPLOAD_SESSION_URL_TEMPLATE.build(this.getAPI().getBaseUploadURL(), this.getID()); 1400 return new LargeFileUpload().upload(this.getAPI(), inputStream, url, fileSize); 1401 } 1402 1403 /** 1404 * Creates a new version of a file. Also sets file attributes. 1405 * 1406 * @param inputStream the stream instance that contains the data. 1407 * @param fileSize the size of the file that will be uploaded. 1408 * @param fileAttributes file attributes to set 1409 * @return the created file instance. 1410 * @throws InterruptedException when a thread execution is interrupted. 1411 * @throws IOException when reading a stream throws exception. 1412 */ 1413 public BoxFile.Info uploadLargeFile(InputStream inputStream, long fileSize, Map<String, String> fileAttributes) 1414 throws InterruptedException, IOException { 1415 URL url = UPLOAD_SESSION_URL_TEMPLATE.build(this.getAPI().getBaseUploadURL(), this.getID()); 1416 return new LargeFileUpload().upload(this.getAPI(), inputStream, url, fileSize, fileAttributes); 1417 } 1418 1419 /** 1420 * Creates a new version of a file using specified number of parallel http connections. 1421 * 1422 * @param inputStream the stream instance that contains the data. 1423 * @param fileSize the size of the file that will be uploaded. 1424 * @param nParallelConnections number of parallel http connections to use 1425 * @param timeOut time to wait before killing the job 1426 * @param unit time unit for the time wait value 1427 * @return the created file instance. 1428 * @throws InterruptedException when a thread execution is interrupted. 1429 * @throws IOException when reading a stream throws exception. 1430 */ 1431 public BoxFile.Info uploadLargeFile(InputStream inputStream, long fileSize, 1432 int nParallelConnections, long timeOut, TimeUnit unit) 1433 throws InterruptedException, IOException { 1434 URL url = UPLOAD_SESSION_URL_TEMPLATE.build(this.getAPI().getBaseUploadURL(), this.getID()); 1435 return new LargeFileUpload(nParallelConnections, timeOut, unit) 1436 .upload(this.getAPI(), inputStream, url, fileSize); 1437 } 1438 1439 /** 1440 * Creates a new version of a file using specified number of parallel http connections. Also sets file attributes. 1441 * 1442 * @param inputStream the stream instance that contains the data. 1443 * @param fileSize the size of the file that will be uploaded. 1444 * @param nParallelConnections number of parallel http connections to use 1445 * @param timeOut time to wait before killing the job 1446 * @param unit time unit for the time wait value 1447 * @param fileAttributes file attributes to set 1448 * @return the created file instance. 1449 * @throws InterruptedException when a thread execution is interrupted. 1450 * @throws IOException when reading a stream throws exception. 1451 */ 1452 public BoxFile.Info uploadLargeFile(InputStream inputStream, long fileSize, 1453 int nParallelConnections, long timeOut, TimeUnit unit, 1454 Map<String, String> fileAttributes) 1455 throws InterruptedException, IOException { 1456 URL url = UPLOAD_SESSION_URL_TEMPLATE.build(this.getAPI().getBaseUploadURL(), this.getID()); 1457 return new LargeFileUpload(nParallelConnections, timeOut, unit) 1458 .upload(this.getAPI(), inputStream, url, fileSize, fileAttributes); 1459 } 1460 1461 private BoxCollaboration.Info collaborate(JsonObject accessibleByField, BoxCollaboration.Role role, 1462 Boolean notify, Boolean canViewPath, Date expiresAt, 1463 Boolean isAccessOnly) { 1464 1465 JsonObject itemField = new JsonObject(); 1466 itemField.add("id", this.getID()); 1467 itemField.add("type", "file"); 1468 1469 return BoxCollaboration.create(this.getAPI(), accessibleByField, itemField, role, notify, canViewPath, 1470 expiresAt, isAccessOnly); 1471 } 1472 1473 /** 1474 * Adds a collaborator to this file. 1475 * 1476 * @param collaborator the collaborator to add. 1477 * @param role the role of the collaborator. 1478 * @param notify determines if the user (or all the users in the group) will receive email notifications. 1479 * @param canViewPath whether view path collaboration feature is enabled or not. 1480 * @param expiresAt when the collaboration should expire. 1481 * @param isAccessOnly whether the collaboration is access only or not. 1482 * @return info about the new collaboration. 1483 */ 1484 public BoxCollaboration.Info collaborate(BoxCollaborator collaborator, BoxCollaboration.Role role, 1485 Boolean notify, Boolean canViewPath, 1486 Date expiresAt, Boolean isAccessOnly) { 1487 JsonObject accessibleByField = new JsonObject(); 1488 accessibleByField.add("id", collaborator.getID()); 1489 1490 if (collaborator instanceof BoxUser) { 1491 accessibleByField.add("type", "user"); 1492 } else if (collaborator instanceof BoxGroup) { 1493 accessibleByField.add("type", "group"); 1494 } else { 1495 throw new IllegalArgumentException("The given collaborator is of an unknown type."); 1496 } 1497 return this.collaborate(accessibleByField, role, notify, canViewPath, expiresAt, isAccessOnly); 1498 } 1499 1500 /** 1501 * Adds a collaborator to this file. 1502 * 1503 * @param collaborator the collaborator to add. 1504 * @param role the role of the collaborator. 1505 * @param notify determines if the user (or all the users in the group) will receive email notifications. 1506 * @param canViewPath whether view path collaboration feature is enabled or not. 1507 * @return info about the new collaboration. 1508 */ 1509 public BoxCollaboration.Info collaborate(BoxCollaborator collaborator, BoxCollaboration.Role role, 1510 Boolean notify, Boolean canViewPath) { 1511 return this.collaborate(collaborator, role, notify, canViewPath, null, null); 1512 } 1513 1514 /** 1515 * Adds a collaborator to this folder. An email will be sent to the collaborator if they don't already have a Box 1516 * account. 1517 * 1518 * @param email the email address of the collaborator to add. 1519 * @param role the role of the collaborator. 1520 * @param notify determines if the user (or all the users in the group) will receive email notifications. 1521 * @param canViewPath whether view path collaboration feature is enabled or not. 1522 * @param expiresAt when the collaboration should expire. 1523 * @param isAccessOnly whether the collaboration is access only or not. 1524 * @return info about the new collaboration. 1525 */ 1526 public BoxCollaboration.Info collaborate(String email, BoxCollaboration.Role role, 1527 Boolean notify, Boolean canViewPath, 1528 Date expiresAt, Boolean isAccessOnly) { 1529 JsonObject accessibleByField = new JsonObject(); 1530 accessibleByField.add("login", email); 1531 accessibleByField.add("type", "user"); 1532 1533 return this.collaborate(accessibleByField, role, notify, canViewPath, expiresAt, isAccessOnly); 1534 } 1535 1536 /** 1537 * Adds a collaborator to this folder. An email will be sent to the collaborator if they don't already have a Box 1538 * account. 1539 * 1540 * @param email the email address of the collaborator to add. 1541 * @param role the role of the collaborator. 1542 * @param notify determines if the user (or all the users in the group) will receive email notifications. 1543 * @param canViewPath whether view path collaboration feature is enabled or not. 1544 * @return info about the new collaboration. 1545 */ 1546 public BoxCollaboration.Info collaborate(String email, BoxCollaboration.Role role, 1547 Boolean notify, Boolean canViewPath) { 1548 return this.collaborate(email, role, notify, canViewPath, null, null); 1549 } 1550 1551 /** 1552 * Used to retrieve all collaborations associated with the item. 1553 * 1554 * @param fields the optional fields to retrieve. 1555 * @return An iterable of metadata instances associated with the item. 1556 */ 1557 public BoxResourceIterable<BoxCollaboration.Info> getAllFileCollaborations(String... fields) { 1558 return BoxCollaboration.getAllFileCollaborations(this.getAPI(), this.getID(), 1559 GET_COLLABORATORS_PAGE_SIZE, fields); 1560 1561 } 1562 1563 /** 1564 * Used to specify what filetype to request for a file thumbnail. 1565 */ 1566 public enum ThumbnailFileType { 1567 /** 1568 * PNG image format. 1569 */ 1570 PNG, 1571 1572 /** 1573 * JPG image format. 1574 */ 1575 JPG 1576 } 1577 1578 /** 1579 * Enumerates the possible permissions that a user can have on a file. 1580 */ 1581 public enum Permission { 1582 /** 1583 * The user can download the file. 1584 */ 1585 CAN_DOWNLOAD("can_download"), 1586 1587 /** 1588 * The user can upload new versions of the file. 1589 */ 1590 CAN_UPLOAD("can_upload"), 1591 1592 /** 1593 * The user can rename the file. 1594 */ 1595 CAN_RENAME("can_rename"), 1596 1597 /** 1598 * The user can delete the file. 1599 */ 1600 CAN_DELETE("can_delete"), 1601 1602 /** 1603 * The user can share the file. 1604 */ 1605 CAN_SHARE("can_share"), 1606 1607 /** 1608 * The user can set the access level for shared links to the file. 1609 */ 1610 CAN_SET_SHARE_ACCESS("can_set_share_access"), 1611 1612 /** 1613 * The user can preview the file. 1614 */ 1615 CAN_PREVIEW("can_preview"), 1616 1617 /** 1618 * The user can comment on the file. 1619 */ 1620 CAN_COMMENT("can_comment"), 1621 1622 /** 1623 * The user can place annotations on this file. 1624 */ 1625 CAN_ANNOTATE("can_annotate"), 1626 1627 /** 1628 * The current user can invite new users to collaborate on this item, and the user can update the role of a 1629 * user already collaborated on this item. 1630 */ 1631 CAN_INVITE_COLLABORATOR("can_invite_collaborator"), 1632 1633 /** 1634 * The user can view all annotations placed on this file. 1635 */ 1636 CAN_VIEW_ANNOTATIONS_ALL("can_view_annotations_all"), 1637 1638 /** 1639 * The user can view annotations placed by themselves on this file. 1640 */ 1641 CAN_VIEW_ANNOTATIONS_SELF("can_view_annotations_self"); 1642 1643 private final String jsonValue; 1644 1645 Permission(String jsonValue) { 1646 this.jsonValue = jsonValue; 1647 } 1648 1649 static Permission fromJSONValue(String jsonValue) { 1650 return Permission.valueOf(jsonValue.toUpperCase()); 1651 } 1652 1653 String toJSONValue() { 1654 return this.jsonValue; 1655 } 1656 } 1657 1658 /** 1659 * Contains information about a BoxFile. 1660 */ 1661 public class Info extends BoxItem.Info { 1662 private String sha1; 1663 private String versionNumber; 1664 private long commentCount; 1665 private EnumSet<Permission> permissions; 1666 private String extension; 1667 private boolean isPackage; 1668 private BoxFileVersion version; 1669 private URL previewLink; 1670 private BoxLock lock; 1671 private boolean isWatermarked; 1672 private boolean isExternallyOwned; 1673 private Map<String, Map<String, Metadata>> metadataMap; 1674 private List<Representation> representations; 1675 private List<String> allowedInviteeRoles; 1676 private Boolean hasCollaborations; 1677 private String uploaderDisplayName; 1678 private BoxClassification classification; 1679 private Date dispositionAt; 1680 private boolean isAccessibleViaSharedLink; 1681 1682 /** 1683 * Constructs an empty Info object. 1684 */ 1685 public Info() { 1686 super(); 1687 } 1688 1689 /** 1690 * Constructs an Info object by parsing information from a JSON string. 1691 * 1692 * @param json the JSON string to parse. 1693 */ 1694 public Info(String json) { 1695 super(json); 1696 } 1697 1698 /** 1699 * Constructs an Info object using an already parsed JSON object. 1700 * 1701 * @param jsonObject the parsed JSON object. 1702 */ 1703 public Info(JsonObject jsonObject) { 1704 super(jsonObject); 1705 } 1706 1707 @Override 1708 public BoxFile getResource() { 1709 return BoxFile.this; 1710 } 1711 1712 /** 1713 * Gets the SHA1 hash of the file. 1714 * 1715 * @return the SHA1 hash of the file. 1716 */ 1717 public String getSha1() { 1718 return this.sha1; 1719 } 1720 1721 /** 1722 * Gets the lock of the file. 1723 * 1724 * @return the lock of the file. 1725 */ 1726 public BoxLock getLock() { 1727 return this.lock; 1728 } 1729 1730 /** 1731 * Gets the current version number of the file. 1732 * 1733 * @return the current version number of the file. 1734 */ 1735 public String getVersionNumber() { 1736 return this.versionNumber; 1737 } 1738 1739 /** 1740 * Gets the number of comments on the file. 1741 * 1742 * @return the number of comments on the file. 1743 */ 1744 public long getCommentCount() { 1745 return this.commentCount; 1746 } 1747 1748 /** 1749 * Gets the permissions that the current user has on the file. 1750 * 1751 * @return the permissions that the current user has on the file. 1752 */ 1753 public EnumSet<Permission> getPermissions() { 1754 return this.permissions; 1755 } 1756 1757 /** 1758 * Gets the extension suffix of the file, excluding the dot. 1759 * 1760 * @return the extension of the file. 1761 */ 1762 public String getExtension() { 1763 return this.extension; 1764 } 1765 1766 /** 1767 * Gets whether or not the file is an OSX package. 1768 * 1769 * @return true if the file is an OSX package; otherwise false. 1770 */ 1771 public boolean getIsPackage() { 1772 return this.isPackage; 1773 } 1774 1775 /** 1776 * Gets the current version details of the file. 1777 * 1778 * @return the current version details of the file. 1779 */ 1780 public BoxFileVersion getVersion() { 1781 return this.version; 1782 } 1783 1784 /** 1785 * Gets the current expiring preview link. 1786 * 1787 * @return the expiring preview link 1788 */ 1789 public URL getPreviewLink() { 1790 return this.previewLink; 1791 } 1792 1793 /** 1794 * Gets flag indicating whether this file is Watermarked. 1795 * 1796 * @return whether the file is watermarked or not 1797 */ 1798 public boolean getIsWatermarked() { 1799 return this.isWatermarked; 1800 } 1801 1802 /** 1803 * Returns the allowed invitee roles for this file item. 1804 * 1805 * @return the list of roles allowed for invited collaborators. 1806 */ 1807 public List<String> getAllowedInviteeRoles() { 1808 return this.allowedInviteeRoles; 1809 } 1810 1811 /** 1812 * Returns the indicator for whether this file item has collaborations. 1813 * 1814 * @return indicator for whether this file item has collaborations. 1815 */ 1816 public Boolean getHasCollaborations() { 1817 return this.hasCollaborations; 1818 } 1819 1820 /** 1821 * Gets the metadata on this file associated with a specified scope and template. 1822 * Makes an attempt to get metadata that was retrieved using getInfo(String ...) method. 1823 * 1824 * @param templateName the metadata template type name. 1825 * @param scope the scope of the template (usually "global" or "enterprise"). 1826 * @return the metadata returned from the server. 1827 */ 1828 public Metadata getMetadata(String templateName, String scope) { 1829 try { 1830 return this.metadataMap.get(scope).get(templateName); 1831 } catch (NullPointerException e) { 1832 return null; 1833 } 1834 } 1835 1836 /** 1837 * Returns the field for indicating whether a file is owned by a user outside the enterprise. 1838 * 1839 * @return indicator for whether or not the file is owned by a user outside the enterprise. 1840 */ 1841 public boolean getIsExternallyOwned() { 1842 return this.isExternallyOwned; 1843 } 1844 1845 /** 1846 * Get file's representations. 1847 * 1848 * @return list of representations 1849 */ 1850 public List<Representation> getRepresentations() { 1851 return this.representations; 1852 } 1853 1854 /** 1855 * Returns user's name at the time of upload. 1856 * 1857 * @return user's name at the time of upload 1858 */ 1859 public String getUploaderDisplayName() { 1860 return this.uploaderDisplayName; 1861 } 1862 1863 /** 1864 * Gets the metadata classification type of this file. 1865 * 1866 * @return the metadata classification type of this file. 1867 */ 1868 public BoxClassification getClassification() { 1869 return this.classification; 1870 } 1871 1872 /** 1873 * Returns the retention expiration timestamp for the given file. 1874 * 1875 * @return Date representing expiration timestamp 1876 */ 1877 public Date getDispositionAt() { 1878 return dispositionAt; 1879 } 1880 1881 /** 1882 * Modifies the retention expiration timestamp for the given file. 1883 * This date cannot be shortened once set on a file. 1884 * 1885 * @param dispositionAt Date representing expiration timestamp 1886 */ 1887 public void setDispositionAt(Date dispositionAt) { 1888 this.dispositionAt = dispositionAt; 1889 this.addPendingChange("disposition_at", BoxDateFormat.format(dispositionAt)); 1890 } 1891 1892 /** 1893 * Returns the flag indicating whether the file is accessible via a shared link. 1894 * 1895 * @return boolean flag indicating whether the file is accessible via a shared link. 1896 */ 1897 public boolean getIsAccessibleViaSharedLink() { 1898 return this.isAccessibleViaSharedLink; 1899 } 1900 1901 @Override 1902 protected void parseJSONMember(JsonObject.Member member) { 1903 super.parseJSONMember(member); 1904 1905 String memberName = member.getName(); 1906 JsonValue value = member.getValue(); 1907 try { 1908 switch (memberName) { 1909 case "sha1": 1910 this.sha1 = value.asString(); 1911 break; 1912 case "version_number": 1913 this.versionNumber = value.asString(); 1914 break; 1915 case "comment_count": 1916 this.commentCount = value.asLong(); 1917 break; 1918 case "permissions": 1919 this.permissions = this.parsePermissions(value.asObject()); 1920 break; 1921 case "extension": 1922 this.extension = value.asString(); 1923 break; 1924 case "is_package": 1925 this.isPackage = value.asBoolean(); 1926 break; 1927 case "has_collaborations": 1928 this.hasCollaborations = value.asBoolean(); 1929 break; 1930 case "is_externally_owned": 1931 this.isExternallyOwned = value.asBoolean(); 1932 break; 1933 case "file_version": 1934 this.version = this.parseFileVersion(value.asObject()); 1935 break; 1936 case "allowed_invitee_roles": 1937 this.allowedInviteeRoles = this.parseAllowedInviteeRoles(value.asArray()); 1938 break; 1939 case "expiring_embed_link": 1940 try { 1941 String urlString = member.getValue().asObject().get("url").asString(); 1942 this.previewLink = new URL(urlString); 1943 } catch (MalformedURLException e) { 1944 throw new BoxAPIException("Couldn't parse expiring_embed_link/url for file", e); 1945 } 1946 break; 1947 case "lock": 1948 if (value.isNull()) { 1949 this.lock = null; 1950 } else { 1951 this.lock = new BoxLock(value.asObject(), BoxFile.this.getAPI()); 1952 } 1953 break; 1954 case "watermark_info": 1955 this.isWatermarked = value.asObject().get("is_watermarked").asBoolean(); 1956 break; 1957 case "metadata": 1958 this.metadataMap = Parsers.parseAndPopulateMetadataMap(value.asObject()); 1959 break; 1960 case "representations": 1961 this.representations = Parsers.parseRepresentations(value.asObject()); 1962 break; 1963 case "uploader_display_name": 1964 this.uploaderDisplayName = value.asString(); 1965 break; 1966 case "classification": 1967 if (value.isNull()) { 1968 this.classification = null; 1969 } else { 1970 this.classification = new BoxClassification(value.asObject()); 1971 } 1972 break; 1973 case "disposition_at": 1974 this.dispositionAt = BoxDateFormat.parse(value.asString()); 1975 break; 1976 case "is_accessible_via_shared_link": 1977 this.isAccessibleViaSharedLink = value.asBoolean(); 1978 break; 1979 default: 1980 break; 1981 } 1982 } catch (Exception e) { 1983 throw new BoxDeserializationException(memberName, value.toString(), e); 1984 } 1985 } 1986 1987 @SuppressWarnings("checkstyle:MissingSwitchDefault") 1988 private EnumSet<Permission> parsePermissions(JsonObject jsonObject) { 1989 EnumSet<Permission> permissions = EnumSet.noneOf(Permission.class); 1990 for (JsonObject.Member member : jsonObject) { 1991 JsonValue value = member.getValue(); 1992 if (value.isNull() || !value.asBoolean()) { 1993 continue; 1994 } 1995 try { 1996 permissions.add(Permission.fromJSONValue(member.getName())); 1997 } catch (IllegalArgumentException ignored) { 1998 // If the permission is not recognized, we ignore it. 1999 } 2000 } 2001 2002 return permissions; 2003 } 2004 2005 private BoxFileVersion parseFileVersion(JsonObject jsonObject) { 2006 return new BoxFileVersion(BoxFile.this.getAPI(), jsonObject, BoxFile.this.getID()); 2007 } 2008 2009 private List<String> parseAllowedInviteeRoles(JsonArray jsonArray) { 2010 List<String> roles = new ArrayList<>(jsonArray.size()); 2011 for (JsonValue value : jsonArray) { 2012 roles.add(value.asString()); 2013 } 2014 2015 return roles; 2016 } 2017 } 2018 2019}