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