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