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