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