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