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"}; 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 Set<BoxCollection.Info> collections; 221 222 /** 223 * Constructs an empty Info object. 224 */ 225 public Info() { 226 super(); 227 } 228 229 /** 230 * Constructs an Info object by parsing information from a JSON string. 231 * @param json the JSON string to parse. 232 */ 233 public Info(String json) { 234 super(json); 235 } 236 237 /** 238 * Constructs an Info object using an already parsed JSON object. 239 * @param jsonObject the parsed JSON object. 240 */ 241 Info(JsonObject jsonObject) { 242 super(jsonObject); 243 } 244 245 /** 246 * Gets the item type. 247 * @return the item's type. 248 */ 249 public String getType() { 250 return this.type; 251 } 252 253 /** 254 * Gets a unique string identifying the version of the item. 255 * @return a unique string identifying the version of the item. 256 */ 257 public String getEtag() { 258 return this.etag; 259 } 260 261 /** 262 * Gets the name of the item. 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 * @param name the new name of the item. 272 */ 273 public void setName(String name) { 274 this.name = name; 275 this.addPendingChange("name", name); 276 } 277 278 /** 279 * Gets the time the item was created. 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 * @return the time the item was last modified. 289 */ 290 public Date getModifiedAt() { 291 return this.modifiedAt; 292 } 293 294 /** 295 * Gets the description of the item. 296 * @return the description of the item. 297 */ 298 public String getDescription() { 299 return this.description; 300 } 301 302 /** 303 * Sets the description of the item. 304 * @param description the new description of the item. 305 */ 306 public void setDescription(String description) { 307 this.description = description; 308 this.addPendingChange("description", description); 309 } 310 311 /** 312 * Gets the size of the item in bytes. 313 * @return the size of the item in bytes. 314 */ 315 public long getSize() { 316 return this.size; 317 } 318 319 /** 320 * Gets the path of folders to the item, starting at the root. 321 * @return the path of folders to the item. 322 */ 323 public List<BoxFolder.Info> getPathCollection() { 324 return this.pathCollection; 325 } 326 327 /** 328 * Gets info about the user who created the item. 329 * @return info about the user who created the item. 330 */ 331 public BoxUser.Info getCreatedBy() { 332 return this.createdBy; 333 } 334 335 /** 336 * Gets info about the user who last modified the item. 337 * @return info about the user who last modified the item. 338 */ 339 public BoxUser.Info getModifiedBy() { 340 return this.modifiedBy; 341 } 342 343 /** 344 * Gets the time that the item was trashed. 345 * @return the time that the item was trashed. 346 */ 347 public Date getTrashedAt() { 348 return this.trashedAt; 349 } 350 351 /** 352 * Gets the time that the item was purged from the trash. 353 * @return the time that the item was purged from the trash. 354 */ 355 public Date getPurgedAt() { 356 return this.purgedAt; 357 } 358 359 /** 360 * Gets the time that the item was created according to the uploader. 361 * @return the time that the item was created according to the uploader. 362 */ 363 public Date getContentCreatedAt() { 364 return this.contentCreatedAt; 365 } 366 367 /** 368 * Gets the time that the item was last modified according to the uploader. 369 * @return the time that the item was last modified according to the uploader. 370 */ 371 public Date getContentModifiedAt() { 372 return this.contentModifiedAt; 373 } 374 375 /** 376 * Gets info about the user who owns the item. 377 * @return info about the user who owns the item. 378 */ 379 public BoxUser.Info getOwnedBy() { 380 return this.ownedBy; 381 } 382 383 /** 384 * Gets the shared link for the item. 385 * @return the shared link for the item. 386 */ 387 public BoxSharedLink getSharedLink() { 388 return this.sharedLink; 389 } 390 391 /** 392 * Sets a shared link for the item. 393 * @param sharedLink the shared link for the item. 394 */ 395 public void setSharedLink(BoxSharedLink sharedLink) { 396 if (this.sharedLink == sharedLink) { 397 return; 398 } 399 400 this.removeChildObject("shared_link"); 401 this.sharedLink = sharedLink; 402 this.addChildObject("shared_link", sharedLink); 403 } 404 405 /** 406 * Gets a unique ID for use with the {@link EventStream}. 407 * @return a unique ID for use with the EventStream. 408 */ 409 public String getSequenceID() { 410 return this.sequenceID; 411 } 412 413 /** 414 * Gets a list of all the tags applied to the item. 415 * 416 * <p>Note that this field isn't populated by default and must be specified as a field parameter when getting 417 * Info about the item.</p> 418 * 419 * @return a list of all the tags applied to the item. 420 */ 421 public List<String> getTags() { 422 return this.tags; 423 } 424 425 /** 426 * Sets the tags for an item. 427 * @param tags The new tags for the item. 428 */ 429 public void setTags(List<String> tags) { 430 this.tags = tags; 431 JsonArray tagsJSON = new JsonArray(); 432 for (String tag : tags) { 433 tagsJSON.add(tag); 434 } 435 this.addPendingChange("tags", tagsJSON); 436 } 437 438 /** 439 * Gets info about the parent folder of the item. 440 * @return info about the parent folder of the item. 441 */ 442 public BoxFolder.Info getParent() { 443 return this.parent; 444 } 445 446 /** 447 * Gets the status of the item. 448 * @return the status of the item. 449 */ 450 public String getItemStatus() { 451 return this.itemStatus; 452 } 453 454 /** 455 * Gets info about the collections that this item belongs to. 456 * @return info about the collections that this item belongs to. 457 */ 458 public Iterable<BoxCollection.Info> getCollections() { 459 return this.collections; 460 } 461 462 /** 463 * Sets the collections that this item belongs to. 464 * @param collections the new list of collections that this item should belong to. 465 */ 466 public void setCollections(Iterable<BoxCollection> collections) { 467 if (this.collections == null) { 468 this.collections = new HashSet<BoxCollection.Info>(); 469 } else { 470 this.collections.clear(); 471 } 472 473 JsonArray jsonArray = new JsonArray(); 474 for (BoxCollection collection : collections) { 475 JsonObject jsonObject = new JsonObject(); 476 jsonObject.add("id", collection.getID()); 477 jsonArray.add(jsonObject); 478 this.collections.add(collection.new Info()); 479 } 480 this.addPendingChange("collections", jsonArray); 481 } 482 483 @Override 484 protected void parseJSONMember(JsonObject.Member member) { 485 super.parseJSONMember(member); 486 487 try { 488 JsonValue value = member.getValue(); 489 String memberName = member.getName(); 490 if (memberName.equals("sequence_id")) { 491 this.sequenceID = value.asString(); 492 } else if (memberName.equals("type")) { 493 this.type = value.asString(); 494 } else if (memberName.equals("etag")) { 495 this.etag = value.asString(); 496 } else if (memberName.equals("name")) { 497 this.name = value.asString(); 498 } else if (memberName.equals("created_at")) { 499 this.createdAt = BoxDateFormat.parse(value.asString()); 500 } else if (memberName.equals("modified_at")) { 501 this.modifiedAt = BoxDateFormat.parse(value.asString()); 502 } else if (memberName.equals("description")) { 503 this.description = value.asString(); 504 } else if (memberName.equals("size")) { 505 this.size = Double.valueOf(value.toString()).longValue(); 506 } else if (memberName.equals("trashed_at")) { 507 this.trashedAt = BoxDateFormat.parse(value.asString()); 508 } else if (memberName.equals("purged_at")) { 509 this.purgedAt = BoxDateFormat.parse(value.asString()); 510 } else if (memberName.equals("content_created_at")) { 511 this.contentCreatedAt = BoxDateFormat.parse(value.asString()); 512 } else if (memberName.equals("content_modified_at")) { 513 this.contentModifiedAt = BoxDateFormat.parse(value.asString()); 514 } else if (memberName.equals("path_collection")) { 515 this.pathCollection = this.parsePathCollection(value.asObject()); 516 } else if (memberName.equals("created_by")) { 517 this.createdBy = this.parseUserInfo(value.asObject()); 518 } else if (memberName.equals("modified_by")) { 519 this.modifiedBy = this.parseUserInfo(value.asObject()); 520 } else if (memberName.equals("owned_by")) { 521 this.ownedBy = this.parseUserInfo(value.asObject()); 522 } else if (memberName.equals("shared_link")) { 523 if (this.sharedLink == null) { 524 this.setSharedLink(new BoxSharedLink(value.asObject())); 525 } else { 526 this.sharedLink.update(value.asObject()); 527 } 528 } else if (memberName.equals("tags")) { 529 this.tags = this.parseTags(value.asArray()); 530 } else if (memberName.equals("parent")) { 531 JsonObject jsonObject = value.asObject(); 532 if (this.parent == null) { 533 String id = jsonObject.get("id").asString(); 534 BoxFolder parentFolder = new BoxFolder(getAPI(), id); 535 this.parent = parentFolder.new Info(jsonObject); 536 } else { 537 this.parent.update(jsonObject); 538 } 539 } else if (memberName.equals("item_status")) { 540 this.itemStatus = value.asString(); 541 } else if (memberName.equals("collections")) { 542 if (this.collections == null) { 543 this.collections = new HashSet<BoxCollection.Info>(); 544 } else { 545 this.collections.clear(); 546 } 547 548 BoxAPIConnection api = getAPI(); 549 JsonArray jsonArray = value.asArray(); 550 for (JsonValue arrayValue : jsonArray) { 551 JsonObject jsonObject = arrayValue.asObject(); 552 String id = jsonObject.get("id").asString(); 553 BoxCollection collection = new BoxCollection(api, id); 554 BoxCollection.Info collectionInfo = collection.new Info(jsonObject); 555 this.collections.add(collectionInfo); 556 } 557 } 558 } catch (ParseException e) { 559 assert false : "A ParseException indicates a bug in the SDK."; 560 } 561 } 562 563 private List<BoxFolder.Info> parsePathCollection(JsonObject jsonObject) { 564 int count = jsonObject.get("total_count").asInt(); 565 List<BoxFolder.Info> pathCollection = new ArrayList<BoxFolder.Info>(count); 566 JsonArray entries = jsonObject.get("entries").asArray(); 567 for (JsonValue value : entries) { 568 JsonObject entry = value.asObject(); 569 String id = entry.get("id").asString(); 570 BoxFolder folder = new BoxFolder(getAPI(), id); 571 pathCollection.add(folder.new Info(entry)); 572 } 573 574 return pathCollection; 575 } 576 577 private BoxUser.Info parseUserInfo(JsonObject jsonObject) { 578 String userID = jsonObject.get("id").asString(); 579 BoxUser user = new BoxUser(getAPI(), userID); 580 return user.new Info(jsonObject); 581 } 582 583 private List<String> parseTags(JsonArray jsonArray) { 584 List<String> tags = new ArrayList<String>(jsonArray.size()); 585 for (JsonValue value : jsonArray) { 586 tags.add(value.asString()); 587 } 588 589 return tags; 590 } 591 } 592}