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