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