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