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