001package com.box.sdk;
002
003import java.net.URL;
004import java.util.ArrayList;
005import java.util.Date;
006import java.util.HashSet;
007import java.util.List;
008import java.util.Set;
009
010import com.eclipsesource.json.JsonArray;
011import com.eclipsesource.json.JsonObject;
012import com.eclipsesource.json.JsonValue;
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()}.
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     * @param  api the API connection to be used by the item.
040     * @param  id  the ID of the item.
041     */
042    public BoxItem(BoxAPIConnection api, String id) {
043        super(api, id);
044    }
045
046    /**
047     * @return URL for the current object, constructed as base URL pus an item specifier.
048     */
049    protected URL getItemURL() {
050        return new URLTemplate("").build(this.getAPI().getBaseURL());
051    }
052
053    /**
054     * Gets an item that was shared with a shared link.
055     * @param  api        the API connection to be used by the shared item.
056     * @param  sharedLink the shared link to the item.
057     * @return            info about the shared item.
058     */
059    public static BoxItem.Info getSharedItem(BoxAPIConnection api, String sharedLink) {
060        return getSharedItem(api, sharedLink, null);
061    }
062
063    /**
064     * Gets an item that was shared with a password-protected shared link.
065     * @param  api        the API connection to be used by the shared item.
066     * @param  sharedLink the shared link to the item.
067     * @param  password   the password for the shared link.
068     * @return            info about the shared item.
069     */
070    public static BoxItem.Info getSharedItem(BoxAPIConnection api, String sharedLink, String password) {
071        BoxAPIConnection newAPI = new SharedLinkAPIConnection(api, sharedLink, password);
072        URL url = SHARED_ITEM_URL_TEMPLATE.build(newAPI.getBaseURL());
073        BoxAPIRequest request = new BoxAPIRequest(newAPI, url, "GET");
074        BoxJSONResponse response = (BoxJSONResponse) request.send();
075        JsonObject json = JsonObject.readFrom(response.getJSON());
076        return (BoxItem.Info) BoxResource.parseInfo(newAPI, json);
077    }
078
079    /**
080     * Used to retrieve the watermark for the item.
081     * If the item does not have a watermark applied to it, a 404 Not Found will be returned by API.
082     * @param itemUrl url template for the item.
083     * @param fields the fields to retrieve.
084     * @return the watermark associated with the item.
085     */
086    protected BoxWatermark getWatermark(URLTemplate itemUrl, String... fields) {
087        URL watermarkUrl = itemUrl.build(this.getAPI().getBaseURL(), this.getID());
088        QueryStringBuilder builder = new QueryStringBuilder();
089        if (fields.length > 0) {
090            builder.appendParam("fields", fields);
091        }
092        URL url = WATERMARK_URL_TEMPLATE.buildWithQuery(watermarkUrl.toString(), builder.toString());
093        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET");
094        BoxJSONResponse response = (BoxJSONResponse) request.send();
095        return new BoxWatermark(response.getJSON());
096    }
097
098    /**
099     * Used to apply or update the watermark for the item.
100     * @param itemUrl url template for the item.
101     * @param imprint the value must be "default", as custom watermarks is not yet supported.
102     * @return the watermark associated with the item.
103     */
104    protected BoxWatermark applyWatermark(URLTemplate itemUrl, String imprint) {
105        URL watermarkUrl = itemUrl.build(this.getAPI().getBaseURL(), this.getID());
106        URL url = WATERMARK_URL_TEMPLATE.build(watermarkUrl.toString());
107        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT");
108        JsonObject body = new JsonObject()
109                .add(BoxWatermark.WATERMARK_JSON_KEY, new JsonObject()
110                        .add(BoxWatermark.WATERMARK_IMPRINT_JSON_KEY, imprint));
111        request.setBody(body.toString());
112        BoxJSONResponse response = (BoxJSONResponse) request.send();
113        return new BoxWatermark(response.getJSON());
114    }
115
116    /**
117     * Removes a watermark from the item.
118     * If the item did not have a watermark applied to it, a 404 Not Found will be returned by API.
119     * @param itemUrl url template for the item.
120     */
121    protected void removeWatermark(URLTemplate itemUrl) {
122        URL watermarkUrl = itemUrl.build(this.getAPI().getBaseURL(), this.getID());
123        URL url = WATERMARK_URL_TEMPLATE.build(watermarkUrl.toString());
124        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "DELETE");
125        BoxAPIResponse response = request.send();
126        response.disconnect();
127    }
128
129    /**
130     * Copies this item to another folder.
131     * @param  destination the destination folder.
132     * @return             info about the copied item.
133     */
134    public abstract BoxItem.Info copy(BoxFolder destination);
135
136    /**
137     * Copies this item to another folder and gives it a new name. If the destination is the same folder as the item's
138     * current parent, then newName must be a new, unique name.
139     * @param  destination the destination folder.
140     * @param  newName     a new name for the copied item.
141     * @return             info about the copied item.
142     */
143    public abstract BoxItem.Info copy(BoxFolder destination, String newName);
144
145    /**
146     * Moves this item to another folder.
147     * @param  destination the destination folder.
148     * @return             info about the moved item.
149     */
150    public abstract BoxItem.Info move(BoxFolder destination);
151
152    /**
153     * Moves this item to another folder and gives it a new name.
154     * @param  destination the destination folder.
155     * @param  newName     a new name for the moved item.
156     * @return             info about the moved item.
157     */
158    public abstract BoxItem.Info move(BoxFolder destination, String newName);
159
160    /**
161     * Creates a new shared link for this item.
162     *
163     * <p>This method is a convenience method for manually creating a new shared link and applying it to this item with
164     * {@link Info#setSharedLink}. You may want to create the shared link manually so that it can be updated along with
165     * other changes to the item's info in a single network request, giving a boost to performance.</p>
166     *
167     * @param  access      the access level of the shared link.
168     * @param  unshareDate the date and time at which the link will expire. Can be null to create a non-expiring link.
169     * @param  permissions the permissions of the shared link. Can be null to use the default permissions.
170     * @return             the created shared link.
171     */
172    public abstract BoxSharedLink createSharedLink(BoxSharedLink.Access access, Date unshareDate,
173        BoxSharedLink.Permissions permissions);
174
175    /**
176     * Gets information about this item.
177     * @return info about this item.
178     */
179    public abstract BoxItem.Info getInfo();
180
181    /**
182     * Gets information about this item that's limited to a list of specified fields.
183     * @param  fields the fields to retrieve.
184     * @return        info about this item containing only the specified fields.
185     */
186    public abstract BoxItem.Info getInfo(String... fields);
187
188    /**
189     * Sets the collections that this item belongs to.
190     * @param   collections the collections that this item should belong to.
191     * @return              info about the item, including the collections it belongs to.
192     */
193    public abstract BoxItem.Info setCollections(BoxCollection... collections);
194
195    /**
196     * Contains information about a BoxItem.
197     */
198    public abstract class Info extends BoxResource.Info {
199        private String type;
200        private String sequenceID;
201        private String etag;
202        private String name;
203        private Date createdAt;
204        private Date modifiedAt;
205        private String description;
206        private long size;
207        private List<BoxFolder.Info> pathCollection;
208        private BoxUser.Info createdBy;
209        private BoxUser.Info modifiedBy;
210        private Date trashedAt;
211        private Date purgedAt;
212        private Date contentCreatedAt;
213        private Date contentModifiedAt;
214        private BoxUser.Info ownedBy;
215        private BoxSharedLink sharedLink;
216        private List<String> tags;
217        private BoxFolder.Info parent;
218        private String itemStatus;
219        private Date expiresAt;
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 the expires at time for this item.
377         * @return the time that the item will expire at.
378         */
379        public Date getExpiresAt() {
380            return this.expiresAt;
381        }
382
383        /**
384         * Gets info about the user who owns the item.
385         * @return info about the user who owns the item.
386         */
387        public BoxUser.Info getOwnedBy() {
388            return this.ownedBy;
389        }
390
391        /**
392         * Gets the shared link for the item.
393         * @return the shared link for the item.
394         */
395        public BoxSharedLink getSharedLink() {
396            return this.sharedLink;
397        }
398
399        /**
400         * Sets a shared link for the item.
401         * @param sharedLink the shared link for the item.
402         */
403        public void setSharedLink(BoxSharedLink sharedLink) {
404            this.removeChildObject("shared_link");
405            this.sharedLink = sharedLink;
406            this.addChildObject("shared_link", sharedLink);
407        }
408
409        /**
410         * Removes the shared link for the item.
411         */
412        public void removeSharedLink() {
413            this.addChildObject("shared_link", null);
414        }
415
416        /**
417         * Gets a unique ID for use with the {@link EventStream}.
418         * @return a unique ID for use with the EventStream.
419         */
420        public String getSequenceID() {
421            return this.sequenceID;
422        }
423
424        /**
425         * Gets a list of all the tags applied to the item.
426         *
427         * <p>Note that this field isn't populated by default and must be specified as a field parameter when getting
428         * Info about the item.</p>
429         *
430         * @return a list of all the tags applied to the item.
431         */
432        public List<String> getTags() {
433            return this.tags;
434        }
435
436        /**
437         * Sets the tags for an item.
438         * @param tags The new tags for the item.
439         */
440        public void setTags(List<String> tags) {
441            this.tags = tags;
442            JsonArray tagsJSON = new JsonArray();
443            for (String tag : tags) {
444                tagsJSON.add(tag);
445            }
446            this.addPendingChange("tags", tagsJSON);
447        }
448
449        /**
450         * Gets info about the parent folder of the item.
451         * @return info about the parent folder of the item.
452         */
453        public BoxFolder.Info getParent() {
454            return this.parent;
455        }
456
457        /**
458         * Gets the status of the item.
459         * @return the status of the item.
460         */
461        public String getItemStatus() {
462            return this.itemStatus;
463        }
464
465        /**
466         * Gets info about the collections that this item belongs to.
467         * @return info about the collections that this item belongs to.
468         */
469        public Iterable<BoxCollection.Info> getCollections() {
470            return this.collections;
471        }
472
473        /**
474         * Sets the collections that this item belongs to.
475         * @param collections the new list of collections that this item should belong to.
476         */
477        public void setCollections(Iterable<BoxCollection> collections) {
478            if (this.collections == null) {
479                this.collections = new HashSet<BoxCollection.Info>();
480            } else {
481                this.collections.clear();
482            }
483
484            JsonArray jsonArray = new JsonArray();
485            for (BoxCollection collection : collections) {
486                JsonObject jsonObject = new JsonObject();
487                jsonObject.add("id", collection.getID());
488                jsonArray.add(jsonObject);
489                this.collections.add(collection.new Info());
490            }
491            this.addPendingChange("collections", jsonArray);
492        }
493
494        @Override
495        protected void parseJSONMember(JsonObject.Member member) {
496            super.parseJSONMember(member);
497            JsonValue value = member.getValue();
498            String memberName = member.getName();
499
500            try {
501                if (memberName.equals("sequence_id")) {
502                    this.sequenceID = value.asString();
503                } else if (memberName.equals("type")) {
504                    this.type = value.asString();
505                } else if (memberName.equals("etag")) {
506                    this.etag = value.asString();
507                } else if (memberName.equals("name")) {
508                    this.name = value.asString();
509                } else if (memberName.equals("created_at")) {
510                    this.createdAt = BoxDateFormat.parse(value.asString());
511                } else if (memberName.equals("modified_at")) {
512                    this.modifiedAt = BoxDateFormat.parse(value.asString());
513                } else if (memberName.equals("description")) {
514                    this.description = value.asString();
515                } else if (memberName.equals("size")) {
516                    this.size = Double.valueOf(value.toString()).longValue();
517                } else if (memberName.equals("trashed_at")) {
518                    this.trashedAt = BoxDateFormat.parse(value.asString());
519                } else if (memberName.equals("purged_at")) {
520                    this.purgedAt = BoxDateFormat.parse(value.asString());
521                } else if (memberName.equals("content_created_at")) {
522                    this.contentCreatedAt = BoxDateFormat.parse(value.asString());
523                } else if (memberName.equals("content_modified_at")) {
524                    this.contentModifiedAt = BoxDateFormat.parse(value.asString());
525                }  else if (memberName.equals("expires_at")) {
526                    this.expiresAt = BoxDateFormat.parse(value.asString());
527                } else if (memberName.equals("path_collection")) {
528                    this.pathCollection = this.parsePathCollection(value.asObject());
529                } else if (memberName.equals("created_by")) {
530                    this.createdBy = this.parseUserInfo(value.asObject());
531                } else if (memberName.equals("modified_by")) {
532                    this.modifiedBy = this.parseUserInfo(value.asObject());
533                } else if (memberName.equals("owned_by")) {
534                    this.ownedBy = this.parseUserInfo(value.asObject());
535                } else if (memberName.equals("shared_link")) {
536                    if (this.sharedLink == null) {
537                        this.setSharedLink(new BoxSharedLink(value.asObject()));
538                    } else {
539                        this.sharedLink.update(value.asObject());
540                    }
541                } else if (memberName.equals("tags")) {
542                    this.tags = this.parseTags(value.asArray());
543                } else if (memberName.equals("parent")) {
544                    JsonObject jsonObject = value.asObject();
545                    if (this.parent == null) {
546                        String id = jsonObject.get("id").asString();
547                        BoxFolder parentFolder = new BoxFolder(getAPI(), id);
548                        this.parent = parentFolder.new Info(jsonObject);
549                    } else {
550                        this.parent.update(jsonObject);
551                    }
552                } else if (memberName.equals("item_status")) {
553                    this.itemStatus = value.asString();
554                } else if (memberName.equals("collections")) {
555                    if (this.collections == null) {
556                        this.collections = new HashSet<BoxCollection.Info>();
557                    } else {
558                        this.collections.clear();
559                    }
560
561                    BoxAPIConnection api = getAPI();
562                    JsonArray jsonArray = value.asArray();
563                    for (JsonValue arrayValue : jsonArray) {
564                        JsonObject jsonObject = arrayValue.asObject();
565                        String id = jsonObject.get("id").asString();
566                        BoxCollection collection = new BoxCollection(api, id);
567                        BoxCollection.Info collectionInfo = collection.new Info(jsonObject);
568                        this.collections.add(collectionInfo);
569                    }
570                }
571            } catch (Exception e) {
572                throw new BoxDeserializationException(memberName, value.toString(), e);
573            }
574        }
575
576        private List<BoxFolder.Info> parsePathCollection(JsonObject jsonObject) {
577            int count = jsonObject.get("total_count").asInt();
578            List<BoxFolder.Info> pathCollection = new ArrayList<BoxFolder.Info>(count);
579            JsonArray entries = jsonObject.get("entries").asArray();
580            for (JsonValue value : entries) {
581                JsonObject entry = value.asObject();
582                String id = entry.get("id").asString();
583                BoxFolder folder = new BoxFolder(getAPI(), id);
584                pathCollection.add(folder.new Info(entry));
585            }
586
587            return pathCollection;
588        }
589
590        private BoxUser.Info parseUserInfo(JsonObject jsonObject) {
591            String userID = jsonObject.get("id").asString();
592            BoxUser user = new BoxUser(getAPI(), userID);
593            return user.new Info(jsonObject);
594        }
595
596        private List<String> parseTags(JsonArray jsonArray) {
597            List<String> tags = new ArrayList<String>(jsonArray.size());
598            for (JsonValue value : jsonArray) {
599                tags.add(value.asString());
600            }
601
602            return tags;
603        }
604    }
605}