001package com.box.sdk; 002 003import com.eclipsesource.json.Json; 004import com.eclipsesource.json.JsonArray; 005import com.eclipsesource.json.JsonObject; 006import com.eclipsesource.json.JsonValue; 007import java.net.URL; 008import java.util.ArrayList; 009import java.util.Date; 010import java.util.HashSet; 011import java.util.List; 012import java.util.Set; 013 014/** The abstract base class for items in a user's file tree (files, folders, etc.). */ 015public abstract class BoxItem extends BoxResource { 016 /** 017 * An array of all possible file fields that can be requested when calling {@link 018 * #getInfo(String...)}. 019 */ 020 public static final String[] ALL_FIELDS = { 021 "type", 022 "id", 023 "sequence_id", 024 "etag", 025 "sha1", 026 "name", 027 "description", 028 "size", 029 "path_collection", 030 "created_at", 031 "modified_at", 032 "trashed_at", 033 "purged_at", 034 "content_created_at", 035 "content_modified_at", 036 "created_by", 037 "modified_by", 038 "owned_by", 039 "shared_link", 040 "parent", 041 "item_status", 042 "version_number", 043 "comment_count", 044 "permissions", 045 "tags", 046 "lock", 047 "extension", 048 "is_package", 049 "folder_upload_email", 050 "item_collection", 051 "sync_state", 052 "has_collaborations", 053 "can_non_owners_invite", 054 "file_version", 055 "collections", 056 "expires_at" 057 }; 058 /** Shared Item URL Template. */ 059 public static final URLTemplate SHARED_ITEM_URL_TEMPLATE = new URLTemplate("shared_items"); 060 061 /** Url template for operations with watermarks. */ 062 public static final URLTemplate WATERMARK_URL_TEMPLATE = new URLTemplate("/watermark"); 063 064 /** 065 * Constructs a BoxItem for an item with a given ID. 066 * 067 * @param api the API connection to be used by the item. 068 * @param id the ID of the item. 069 */ 070 public BoxItem(BoxAPIConnection api, String id) { 071 super(api, id); 072 } 073 074 /** 075 * Gets an item that was shared with a shared link. 076 * 077 * @param api the API connection to be used by the shared item. 078 * @param sharedLink the shared link to the item. 079 * @return info about the shared item. 080 */ 081 public static BoxItem.Info getSharedItem(BoxAPIConnection api, String sharedLink) { 082 return getSharedItem(api, sharedLink, null); 083 } 084 085 /** 086 * Gets an item that was shared with a password-protected shared link. 087 * 088 * @param api the API connection to be used by the shared item. 089 * @param sharedLink the shared link to the item. 090 * @param password the password for the shared link. Use `null` if shared link has no password. 091 * @param fields the fields to retrieve. 092 * @return info about the shared item. 093 */ 094 public static BoxItem.Info getSharedItem( 095 BoxAPIConnection api, String sharedLink, String password, String... fields) { 096 QueryStringBuilder builder = new QueryStringBuilder(); 097 if (fields.length > 0) { 098 builder.appendParam("fields", fields); 099 } 100 URL url = SHARED_ITEM_URL_TEMPLATE.buildWithQuery(api.getBaseURL(), builder.toString()); 101 BoxJSONRequest request = new BoxJSONRequest(api, url, "GET"); 102 103 request.addHeader("BoxApi", BoxSharedLink.getSharedLinkHeaderValue(sharedLink, password)); 104 105 try (BoxJSONResponse response = request.send()) { 106 JsonObject json = Json.parse(response.getJSON()).asObject(); 107 return (BoxItem.Info) BoxResource.parseInfo(api, json); 108 } 109 } 110 111 /** @return URL for the current object, constructed as base URL pus an item specifier. */ 112 protected URL getItemURL() { 113 return new URLTemplate("").build(this.getAPI().getBaseURL()); 114 } 115 116 /** 117 * Used to retrieve the watermark for the item. If the item does not have a watermark applied to 118 * it, a 404 Not Found will be returned by API. 119 * 120 * @param itemUrl url template for the item. 121 * @param fields the fields to retrieve. 122 * @return the watermark associated with the item. 123 */ 124 protected BoxWatermark getWatermark(URLTemplate itemUrl, String... fields) { 125 URL watermarkUrl = itemUrl.build(this.getAPI().getBaseURL(), this.getID()); 126 QueryStringBuilder builder = new QueryStringBuilder(); 127 if (fields.length > 0) { 128 builder.appendParam("fields", fields); 129 } 130 URL url = WATERMARK_URL_TEMPLATE.buildWithQuery(watermarkUrl.toString(), builder.toString()); 131 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "GET"); 132 try (BoxJSONResponse response = request.send()) { 133 return new BoxWatermark(response.getJSON()); 134 } 135 } 136 137 /** 138 * Used to apply or update the watermark for the item. 139 * 140 * @param itemUrl url template for the item. 141 * @param imprint the value must be "default", as custom watermarks is not yet supported. 142 * @return the watermark associated with the item. 143 */ 144 protected BoxWatermark applyWatermark(URLTemplate itemUrl, String imprint) { 145 URL watermarkUrl = itemUrl.build(this.getAPI().getBaseURL(), this.getID()); 146 URL url = WATERMARK_URL_TEMPLATE.build(watermarkUrl.toString()); 147 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT"); 148 JsonObject body = 149 new JsonObject() 150 .add( 151 BoxWatermark.WATERMARK_JSON_KEY, 152 new JsonObject().add(BoxWatermark.WATERMARK_IMPRINT_JSON_KEY, imprint)); 153 request.setBody(body.toString()); 154 try (BoxJSONResponse response = request.send()) { 155 return new BoxWatermark(response.getJSON()); 156 } 157 } 158 159 /** 160 * Removes a watermark from the item. If the item did not have a watermark applied to it, a 404 161 * Not Found will be returned by API. 162 * 163 * @param itemUrl url template for the item. 164 */ 165 protected void removeWatermark(URLTemplate itemUrl) { 166 URL watermarkUrl = itemUrl.build(this.getAPI().getBaseURL(), this.getID()); 167 URL url = WATERMARK_URL_TEMPLATE.build(watermarkUrl.toString()); 168 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "DELETE"); 169 request.send().close(); 170 } 171 172 /** 173 * Copies this item to another folder. 174 * 175 * @param destination the destination folder. 176 * @return info about the copied item. 177 */ 178 public abstract BoxItem.Info copy(BoxFolder destination); 179 180 /** 181 * Copies this item to another folder and gives it a new name. If the destination is the same 182 * folder as the item's current parent, then newName must be a new, unique name. 183 * 184 * @param destination the destination folder. 185 * @param newName a new name for the copied item. 186 * @return info about the copied item. 187 */ 188 public abstract BoxItem.Info copy(BoxFolder destination, String newName); 189 190 /** 191 * Moves this item to another folder. 192 * 193 * @param destination the destination folder. 194 * @return info about the moved item. 195 */ 196 public abstract BoxItem.Info move(BoxFolder destination); 197 198 /** 199 * Moves this item to another folder and gives it a new name. 200 * 201 * @param destination the destination folder. 202 * @param newName a new name for the moved item. 203 * @return info about the moved item. 204 */ 205 public abstract BoxItem.Info move(BoxFolder destination, String newName); 206 207 /** 208 * Gets information about this item that's limited to a list of specified fields. 209 * 210 * @param fields the fields to retrieve. 211 * @return info about this item containing only the specified fields. 212 */ 213 public abstract BoxItem.Info getInfo(String... fields); 214 215 /** 216 * Sets the collections that this item belongs to. 217 * 218 * @param collections the collections that this item should belong to. 219 * @return info about the item, including the collections it belongs to. 220 */ 221 public abstract BoxItem.Info setCollections(BoxCollection... collections); 222 223 /** Contains information about a BoxItem. */ 224 public abstract class Info extends BoxResource.Info { 225 private String type; 226 private String sequenceID; 227 private String etag; 228 private String name; 229 private Date createdAt; 230 private Date modifiedAt; 231 private String description; 232 private long size; 233 private List<BoxFolder.Info> pathCollection; 234 private BoxUser.Info createdBy; 235 private BoxUser.Info modifiedBy; 236 private Date trashedAt; 237 private Date purgedAt; 238 private Date contentCreatedAt; 239 private Date contentModifiedAt; 240 private BoxUser.Info ownedBy; 241 private BoxSharedLink sharedLink; 242 private List<String> tags; 243 private BoxFolder.Info parent; 244 private String itemStatus; 245 private Date expiresAt; 246 private Set<BoxCollection.Info> collections; 247 private String downloadUrl; 248 249 /** Constructs an empty Info object. */ 250 public Info() { 251 super(); 252 } 253 254 /** 255 * Constructs an Info object by parsing information from a JSON string. 256 * 257 * @param json the JSON string to parse. 258 */ 259 public Info(String json) { 260 super(json); 261 } 262 263 /** 264 * Constructs an Info object using an already parsed JSON object. 265 * 266 * @param jsonObject the parsed JSON object. 267 */ 268 Info(JsonObject jsonObject) { 269 super(jsonObject); 270 } 271 272 /** 273 * Gets the item type. 274 * 275 * @return the item's type. 276 */ 277 public String getType() { 278 return this.type; 279 } 280 281 /** 282 * Gets a unique string identifying the version of the item. 283 * 284 * @return a unique string identifying the version of the item. 285 */ 286 public String getEtag() { 287 return this.etag; 288 } 289 290 /** 291 * Gets the name of the item. 292 * 293 * @return the name of the item. 294 */ 295 public String getName() { 296 return this.name; 297 } 298 299 /** 300 * Sets the name of the item. 301 * 302 * @param name the new name of the item. 303 */ 304 public void setName(String name) { 305 this.name = name; 306 this.addPendingChange("name", name); 307 } 308 309 /** 310 * Gets the time the item was created. 311 * 312 * @return the time the item was created. 313 */ 314 public Date getCreatedAt() { 315 return this.createdAt; 316 } 317 318 /** 319 * Gets the time the item was last modified. 320 * 321 * @return the time the item was last modified. 322 */ 323 public Date getModifiedAt() { 324 return this.modifiedAt; 325 } 326 327 /** 328 * Gets the description of the item. 329 * 330 * @return the description of the item. 331 */ 332 public String getDescription() { 333 return this.description; 334 } 335 336 /** 337 * Sets the description of the item. 338 * 339 * @param description the new description of the item. 340 */ 341 public void setDescription(String description) { 342 this.description = description; 343 this.addPendingChange("description", description); 344 } 345 346 /** 347 * Gets the size of the item in bytes. 348 * 349 * @return the size of the item in bytes. 350 */ 351 public long getSize() { 352 return this.size; 353 } 354 355 /** 356 * Gets the path of folders to the item, starting at the root. 357 * 358 * @return the path of folders to the item. 359 */ 360 public List<BoxFolder.Info> getPathCollection() { 361 return this.pathCollection; 362 } 363 364 /** 365 * Gets info about the user who created the item. 366 * 367 * @return info about the user who created the item. 368 */ 369 public BoxUser.Info getCreatedBy() { 370 return this.createdBy; 371 } 372 373 /** 374 * Gets info about the user who last modified the item. 375 * 376 * @return info about the user who last modified the item. 377 */ 378 public BoxUser.Info getModifiedBy() { 379 return this.modifiedBy; 380 } 381 382 /** 383 * Gets the time that the item was trashed. 384 * 385 * @return the time that the item was trashed. 386 */ 387 public Date getTrashedAt() { 388 return this.trashedAt; 389 } 390 391 /** 392 * Gets the time that the item was purged from the trash. 393 * 394 * @return the time that the item was purged from the trash. 395 */ 396 public Date getPurgedAt() { 397 return this.purgedAt; 398 } 399 400 /** 401 * Gets the time that the item was created according to the uploader. 402 * 403 * @return the time that the item was created according to the uploader. 404 */ 405 public Date getContentCreatedAt() { 406 return this.contentCreatedAt; 407 } 408 409 /** 410 * Gets the time that the item was last modified according to the uploader. 411 * 412 * @return the time that the item was last modified according to the uploader. 413 */ 414 public Date getContentModifiedAt() { 415 return this.contentModifiedAt; 416 } 417 418 /** 419 * Gets the expires at time for this item. 420 * 421 * @return the time that the item will expire at. 422 */ 423 public Date getExpiresAt() { 424 return this.expiresAt; 425 } 426 427 /** 428 * Gets info about the user who owns the item. 429 * 430 * @return info about the user who owns the item. 431 */ 432 public BoxUser.Info getOwnedBy() { 433 return this.ownedBy; 434 } 435 436 /** 437 * Gets the shared link for the item. 438 * 439 * @return the shared link for the item. 440 */ 441 public BoxSharedLink getSharedLink() { 442 return this.sharedLink; 443 } 444 445 /** 446 * Sets a shared link for the item. 447 * 448 * @param sharedLink the shared link for the item. 449 */ 450 public void setSharedLink(BoxSharedLink sharedLink) { 451 this.removeChildObject("shared_link"); 452 this.sharedLink = sharedLink; 453 this.addChildObject("shared_link", sharedLink); 454 } 455 456 /** Removes the shared link for the item. */ 457 public void removeSharedLink() { 458 this.addChildObject("shared_link", null); 459 } 460 461 /** 462 * Gets a unique ID for use with the {@link EventStream}. 463 * 464 * @return a unique ID for use with the EventStream. 465 */ 466 public String getSequenceID() { 467 return this.sequenceID; 468 } 469 470 /** 471 * Gets a list of all the tags applied to the item. 472 * 473 * <p>Note that this field isn't populated by default and must be specified as a field parameter 474 * when getting Info about the item. 475 * 476 * @return a list of all the tags applied to the item. 477 */ 478 public List<String> getTags() { 479 return this.tags; 480 } 481 482 /** 483 * Sets the tags for an item. 484 * 485 * @param tags The new tags for the item. 486 */ 487 public void setTags(List<String> tags) { 488 this.tags = tags; 489 JsonArray tagsJSON = new JsonArray(); 490 for (String tag : tags) { 491 tagsJSON.add(tag); 492 } 493 this.addPendingChange("tags", tagsJSON); 494 } 495 496 /** 497 * Gets info about the parent folder of the item. 498 * 499 * @return info about the parent folder of the item. 500 */ 501 public BoxFolder.Info getParent() { 502 return this.parent; 503 } 504 505 /** 506 * Gets the status of the item. 507 * 508 * @return the status of the item. 509 */ 510 public String getItemStatus() { 511 return this.itemStatus; 512 } 513 514 /** 515 * Gets info about the collections that this item belongs to. 516 * 517 * @return info about the collections that this item belongs to. 518 */ 519 public Iterable<BoxCollection.Info> getCollections() { 520 return this.collections; 521 } 522 523 /** 524 * * Gets URL that can be used to download the file. 525 * 526 * @return 527 */ 528 public String getDownloadUrl() { 529 return this.downloadUrl; 530 } 531 532 /** 533 * Sets the collections that this item belongs to. 534 * 535 * @param collections the new list of collections that this item should belong to. 536 */ 537 public void setCollections(Iterable<BoxCollection> collections) { 538 if (this.collections == null) { 539 this.collections = new HashSet<>(); 540 } else { 541 this.collections.clear(); 542 } 543 544 JsonArray jsonArray = new JsonArray(); 545 for (BoxCollection collection : collections) { 546 JsonObject jsonObject = new JsonObject(); 547 jsonObject.add("id", collection.getID()); 548 jsonArray.add(jsonObject); 549 this.collections.add(collection.new Info()); 550 } 551 this.addPendingChange("collections", jsonArray); 552 } 553 554 @Override 555 protected void parseJSONMember(JsonObject.Member member) { 556 super.parseJSONMember(member); 557 JsonValue value = member.getValue(); 558 String memberName = member.getName(); 559 560 try { 561 switch (memberName) { 562 case "sequence_id": 563 this.sequenceID = value.asString(); 564 break; 565 case "type": 566 this.type = value.asString(); 567 break; 568 case "etag": 569 this.etag = value.asString(); 570 break; 571 case "name": 572 this.name = value.asString(); 573 break; 574 case "created_at": 575 this.createdAt = BoxDateFormat.parse(value.asString()); 576 break; 577 case "modified_at": 578 this.modifiedAt = BoxDateFormat.parse(value.asString()); 579 break; 580 case "description": 581 this.description = value.asString(); 582 break; 583 case "size": 584 this.size = Double.valueOf(value.toString()).longValue(); 585 break; 586 case "trashed_at": 587 this.trashedAt = BoxDateFormat.parse(value.asString()); 588 break; 589 case "purged_at": 590 this.purgedAt = BoxDateFormat.parse(value.asString()); 591 break; 592 case "content_created_at": 593 this.contentCreatedAt = BoxDateFormat.parse(value.asString()); 594 break; 595 case "content_modified_at": 596 this.contentModifiedAt = BoxDateFormat.parse(value.asString()); 597 break; 598 case "expires_at": 599 this.expiresAt = BoxDateFormat.parse(value.asString()); 600 break; 601 case "path_collection": 602 this.pathCollection = this.parsePathCollection(value.asObject()); 603 break; 604 case "created_by": 605 this.createdBy = this.parseUserInfo(value.asObject()); 606 break; 607 case "modified_by": 608 this.modifiedBy = this.parseUserInfo(value.asObject()); 609 break; 610 case "owned_by": 611 this.ownedBy = this.parseUserInfo(value.asObject()); 612 break; 613 case "shared_link": 614 if (this.sharedLink == null) { 615 this.setSharedLink(new BoxSharedLink(value.asObject())); 616 } else { 617 this.sharedLink.update(value.asObject()); 618 } 619 break; 620 case "tags": 621 this.tags = this.parseTags(value.asArray()); 622 break; 623 case "parent": 624 JsonObject parentObject = value.asObject(); 625 if (this.parent == null) { 626 String id = parentObject.get("id").asString(); 627 BoxFolder parentFolder = new BoxFolder(getAPI(), id); 628 this.parent = parentFolder.new Info(parentObject); 629 } else { 630 this.parent.update(parentObject); 631 } 632 break; 633 case "item_status": 634 this.itemStatus = value.asString(); 635 break; 636 case "collections": 637 if (this.collections == null) { 638 this.collections = new HashSet<>(); 639 } else { 640 this.collections.clear(); 641 } 642 643 BoxAPIConnection api = getAPI(); 644 JsonArray jsonArray = value.asArray(); 645 for (JsonValue arrayValue : jsonArray) { 646 JsonObject jsonObject = arrayValue.asObject(); 647 String id = jsonObject.get("id").asString(); 648 BoxCollection collection = new BoxCollection(api, id); 649 BoxCollection.Info collectionInfo = collection.new Info(jsonObject); 650 this.collections.add(collectionInfo); 651 } 652 break; 653 case "download_url": 654 this.downloadUrl = value.asString(); 655 break; 656 default: 657 break; 658 } 659 } catch (Exception e) { 660 throw new BoxDeserializationException(memberName, value.toString(), e); 661 } 662 } 663 664 private List<BoxFolder.Info> parsePathCollection(JsonObject jsonObject) { 665 int count = jsonObject.get("total_count").asInt(); 666 List<BoxFolder.Info> pathCollection = new ArrayList<>(count); 667 JsonArray entries = jsonObject.get("entries").asArray(); 668 for (JsonValue value : entries) { 669 JsonObject entry = value.asObject(); 670 String id = entry.get("id").asString(); 671 BoxFolder folder = new BoxFolder(getAPI(), id); 672 pathCollection.add(folder.new Info(entry)); 673 } 674 675 return pathCollection; 676 } 677 678 private BoxUser.Info parseUserInfo(JsonObject jsonObject) { 679 String userID = jsonObject.get("id").asString(); 680 BoxUser user = new BoxUser(getAPI(), userID); 681 return user.new Info(jsonObject); 682 } 683 684 private List<String> parseTags(JsonArray jsonArray) { 685 List<String> tags = new ArrayList<>(jsonArray.size()); 686 for (JsonValue value : jsonArray) { 687 tags.add(value.asString()); 688 } 689 690 return tags; 691 } 692 } 693}