001package com.box.sdk;
002
003import java.net.URL;
004import java.text.ParseException;
005import java.util.ArrayList;
006import java.util.Date;
007import java.util.List;
008
009import com.eclipsesource.json.JsonArray;
010import com.eclipsesource.json.JsonObject;
011import com.eclipsesource.json.JsonValue;
012
013/**
014 * The abstract base class for items in a user's file tree (files, folders, etc.).
015 */
016public abstract class BoxItem extends BoxResource {
017    /**
018     * An array of all possible file fields that can be requested when calling {@link #getInfo()}.
019     */
020    public static final String[] ALL_FIELDS = {"type", "id", "sequence_id", "etag", "sha1", "name", "description",
021        "size", "path_collection", "created_at", "modified_at", "trashed_at", "purged_at", "content_created_at",
022        "content_modified_at", "created_by", "modified_by", "owned_by", "shared_link", "parent", "item_status",
023        "version_number", "comment_count", "permissions", "tags", "lock", "extension", "is_package",
024        "folder_upload_email", "item_collection", "sync_state", "has_collaborations", "can_non_owners_invite",
025        "file_version"};
026
027    private static final URLTemplate SHARED_ITEM_URL_TEMPLATE = new URLTemplate("shared_items");
028
029    /**
030     * Constructs a BoxItem for an item with a given ID.
031     * @param  api the API connection to be used by the item.
032     * @param  id  the ID of the item.
033     */
034    public BoxItem(BoxAPIConnection api, String id) {
035        super(api, id);
036    }
037
038    /**
039     * Gets an item that was shared with a shared link.
040     * @param  api        the API connection to be used by the shared item.
041     * @param  sharedLink the shared link to the item.
042     * @return            info about the shared item.
043     */
044    public static BoxItem.Info getSharedItem(BoxAPIConnection api, String sharedLink) {
045        return getSharedItem(api, sharedLink, null);
046    }
047
048    /**
049     * Gets an item that was shared with a password-protected shared link.
050     * @param  api        the API connection to be used by the shared item.
051     * @param  sharedLink the shared link to the item.
052     * @param  password   the password for the shared link.
053     * @return            info about the shared item.
054     */
055    public static BoxItem.Info getSharedItem(BoxAPIConnection api, String sharedLink, String password) {
056        BoxAPIConnection newAPI = new SharedLinkAPIConnection(api, sharedLink, password);
057        URL url = SHARED_ITEM_URL_TEMPLATE.build(newAPI.getBaseURL());
058        BoxAPIRequest request = new BoxAPIRequest(newAPI, url, "GET");
059        BoxJSONResponse response = (BoxJSONResponse) request.send();
060        JsonObject json = JsonObject.readFrom(response.getJSON());
061        return (BoxItem.Info) BoxResource.parseInfo(newAPI, json);
062    }
063
064    /**
065     * Copies this item to another folder.
066     * @param  destination the destination folder.
067     * @return             info about the copied item.
068     */
069    public abstract BoxItem.Info copy(BoxFolder destination);
070
071    /**
072     * Copies this item to another folder and gives it a new name. If the destination is the same folder as the item's
073     * current parent, then newName must be a new, unique name.
074     * @param  destination the destination folder.
075     * @param  newName     a new name for the copied item.
076     * @return             info about the copied item.
077     */
078    public abstract BoxItem.Info copy(BoxFolder destination, String newName);
079
080    /**
081     * Moves this item to another folder.
082     * @param  destination the destination folder.
083     * @return             info about the moved item.
084     */
085    public abstract BoxItem.Info move(BoxFolder destination);
086
087    /**
088     * Moves this item to another folder and gives it a new name.
089     * @param  destination the destination folder.
090     * @param  newName     a new name for the moved item.
091     * @return             info about the moved item.
092     */
093    public abstract BoxItem.Info move(BoxFolder destination, String newName);
094
095    /**
096     * Creates a new shared link for this item.
097     *
098     * <p>This method is a convenience method for manually creating a new shared link and applying it to this item with
099     * {@link Info#setSharedLink}. You may want to create the shared link manually so that it can be updated along with
100     * other changes to the item's info in a single network request, giving a boost to performance.</p>
101     *
102     * @param  access      the access level of the shared link.
103     * @param  unshareDate the date and time at which the link will expire. Can be null to create a non-expiring link.
104     * @param  permissions the permissions of the shared link. Can be null to use the default permissions.
105     * @return             the created shared link.
106     */
107    public abstract BoxSharedLink createSharedLink(BoxSharedLink.Access access, Date unshareDate,
108        BoxSharedLink.Permissions permissions);
109
110    /**
111     * Gets information about this item.
112     * @return info about this item.
113     */
114    public abstract BoxItem.Info getInfo();
115
116    /**
117     * Gets information about this item that's limited to a list of specified fields.
118     * @param  fields the fields to retrieve.
119     * @return        info about this item containing only the specified fields.
120     */
121    public abstract BoxItem.Info getInfo(String... fields);
122
123    /**
124     * Contains information about a BoxItem.
125     */
126    public abstract class Info extends BoxResource.Info {
127        private String sequenceID;
128        private String etag;
129        private String name;
130        private Date createdAt;
131        private Date modifiedAt;
132        private String description;
133        private long size;
134        private List<BoxFolder.Info> pathCollection;
135        private BoxUser.Info createdBy;
136        private BoxUser.Info modifiedBy;
137        private Date trashedAt;
138        private Date purgedAt;
139        private Date contentCreatedAt;
140        private Date contentModifiedAt;
141        private BoxUser.Info ownedBy;
142        private BoxSharedLink sharedLink;
143        private List<String> tags;
144        private BoxFolder.Info parent;
145        private String itemStatus;
146
147        /**
148         * Constructs an empty Info object.
149         */
150        public Info() {
151            super();
152        }
153
154        /**
155         * Constructs an Info object by parsing information from a JSON string.
156         * @param  json the JSON string to parse.
157         */
158        public Info(String json) {
159            super(json);
160        }
161
162        /**
163         * Constructs an Info object using an already parsed JSON object.
164         * @param  jsonObject the parsed JSON object.
165         */
166        Info(JsonObject jsonObject) {
167            super(jsonObject);
168        }
169
170        /**
171         * Gets a unique string identifying the version of the item.
172         * @return a unique string identifying the version of the item.
173         */
174        public String getEtag() {
175            return this.etag;
176        }
177
178        /**
179         * Gets the name of the item.
180         * @return the name of the item.
181         */
182        public String getName() {
183            return this.name;
184        }
185
186        /**
187         * Sets the name of the item.
188         * @param name the new name of the item.
189         */
190        public void setName(String name) {
191            this.name = name;
192            this.addPendingChange("name", name);
193        }
194
195        /**
196         * Gets the time the item was created.
197         * @return the time the item was created.
198         */
199        public Date getCreatedAt() {
200            return this.createdAt;
201        }
202
203        /**
204         * Gets the time the item was last modified.
205         * @return the time the item was last modified.
206         */
207        public Date getModifiedAt() {
208            return this.modifiedAt;
209        }
210
211        /**
212         * Gets the description of the item.
213         * @return the description of the item.
214         */
215        public String getDescription() {
216            return this.description;
217        }
218
219        /**
220         * Sets the description of the item.
221         * @param description the new description of the item.
222         */
223        public void setDescription(String description) {
224            this.description = description;
225            this.addPendingChange("description", description);
226        }
227
228        /**
229         * Gets the size of the item in bytes.
230         * @return the size of the item in bytes.
231         */
232        public long getSize() {
233            return this.size;
234        }
235
236        /**
237         * Gets the path of folders to the item, starting at the root.
238         * @return the path of folders to the item.
239         */
240        public List<BoxFolder.Info> getPathCollection() {
241            return this.pathCollection;
242        }
243
244        /**
245         * Gets info about the user who created the item.
246         * @return info about the user who created the item.
247         */
248        public BoxUser.Info getCreatedBy() {
249            return this.createdBy;
250        }
251
252        /**
253         * Gets info about the user who last modified the item.
254         * @return info about the user who last modified the item.
255         */
256        public BoxUser.Info getModifiedBy() {
257            return this.modifiedBy;
258        }
259
260        /**
261         * Gets the time that the item was trashed.
262         * @return the time that the item was trashed.
263         */
264        public Date getTrashedAt() {
265            return this.trashedAt;
266        }
267
268        /**
269         * Gets the time that the item was purged from the trash.
270         * @return the time that the item was purged from the trash.
271         */
272        public Date getPurgedAt() {
273            return this.purgedAt;
274        }
275
276        /**
277         * Gets the time that the item was created according to the uploader.
278         * @return the time that the item was created according to the uploader.
279         */
280        public Date getContentCreatedAt() {
281            return this.contentCreatedAt;
282        }
283
284        /**
285         * Gets the time that the item was last modified according to the uploader.
286         * @return the time that the item was last modified according to the uploader.
287         */
288        public Date getContentModifiedAt() {
289            return this.contentModifiedAt;
290        }
291
292        /**
293         * Gets info about the user who owns the item.
294         * @return info about the user who owns the item.
295         */
296        public BoxUser.Info getOwnedBy() {
297            return this.ownedBy;
298        }
299
300        /**
301         * Gets the shared link for the item.
302         * @return the shared link for the item.
303         */
304        public BoxSharedLink getSharedLink() {
305            return this.sharedLink;
306        }
307
308        /**
309         * Sets a shared link for the item.
310         * @param sharedLink the shared link for the item.
311         */
312        public void setSharedLink(BoxSharedLink sharedLink) {
313            if (this.sharedLink == sharedLink) {
314                return;
315            }
316
317            this.removeChildObject("shared_link");
318            this.sharedLink = sharedLink;
319            this.addChildObject("shared_link", sharedLink);
320        }
321
322        /**
323         * Gets a unique ID for use with the {@link EventStream}.
324         * @return a unique ID for use with the EventStream.
325         */
326        public String getSequenceID() {
327            return this.sequenceID;
328        }
329
330        /**
331         * Gets a list of all the tags applied to the item.
332         *
333         * <p>Note that this field isn't populated by default and must be specified as a field parameter when getting
334         * Info about the item.</p>
335         *
336         * @return a list of all the tags applied to the item.
337         */
338        public List<String> getTags() {
339            return this.tags;
340        }
341
342        /**
343         * Gets info about the parent folder of the item.
344         * @return info abou thte parent folder of the item.
345         */
346        public BoxFolder.Info getParent() {
347            return this.parent;
348        }
349
350        /**
351         * Gets the status of the item.
352         * @return the status of the item.
353         */
354        public String getItemStatus() {
355            return this.itemStatus;
356        }
357
358        @Override
359        protected void parseJSONMember(JsonObject.Member member) {
360            super.parseJSONMember(member);
361
362            try {
363                JsonValue value = member.getValue();
364                String memberName = member.getName();
365                if (memberName.equals("sequence_id")) {
366                    this.sequenceID = value.asString();
367                } else if (memberName.equals("etag")) {
368                    this.etag = value.asString();
369                } else if (memberName.equals("name")) {
370                    this.name = value.asString();
371                } else if (memberName.equals("created_at")) {
372                    this.createdAt = BoxDateFormat.parse(value.asString());
373                } else if (memberName.equals("modified_at")) {
374                    this.modifiedAt = BoxDateFormat.parse(value.asString());
375                } else if (memberName.equals("description")) {
376                    this.description = value.asString();
377                } else if (memberName.equals("size")) {
378                    this.size = Double.valueOf(value.toString()).longValue();
379                } else if (memberName.equals("trashed_at")) {
380                    this.trashedAt = BoxDateFormat.parse(value.asString());
381                } else if (memberName.equals("purged_at")) {
382                    this.purgedAt = BoxDateFormat.parse(value.asString());
383                } else if (memberName.equals("content_created_at")) {
384                    this.contentCreatedAt = BoxDateFormat.parse(value.asString());
385                } else if (memberName.equals("content_modified_at")) {
386                    this.contentModifiedAt = BoxDateFormat.parse(value.asString());
387                } else if (memberName.equals("path_collection")) {
388                    this.pathCollection = this.parsePathCollection(value.asObject());
389                } else if (memberName.equals("created_by")) {
390                    this.createdBy = this.parseUserInfo(value.asObject());
391                } else if (memberName.equals("modified_by")) {
392                    this.modifiedBy = this.parseUserInfo(value.asObject());
393                } else if (memberName.equals("owned_by")) {
394                    this.ownedBy = this.parseUserInfo(value.asObject());
395                } else if (memberName.equals("shared_link")) {
396                    if (this.sharedLink == null) {
397                        this.setSharedLink(new BoxSharedLink(value.asObject()));
398                    } else {
399                        this.sharedLink.update(value.asObject());
400                    }
401                } else if (memberName.equals("tags")) {
402                    this.tags = this.parseTags(value.asArray());
403                } else if (memberName.equals("parent")) {
404                    JsonObject jsonObject = value.asObject();
405                    if (this.parent == null) {
406                        String id = jsonObject.get("id").asString();
407                        BoxFolder parentFolder = new BoxFolder(getAPI(), id);
408                        this.parent = parentFolder.new Info(jsonObject);
409                    } else {
410                        this.parent.update(jsonObject);
411                    }
412                } else if (memberName.equals("item_status")) {
413                    this.itemStatus = value.asString();
414                }
415            } catch (ParseException e) {
416                assert false : "A ParseException indicates a bug in the SDK.";
417            }
418        }
419
420        private List<BoxFolder.Info> parsePathCollection(JsonObject jsonObject) {
421            int count = jsonObject.get("total_count").asInt();
422            List<BoxFolder.Info> pathCollection = new ArrayList<BoxFolder.Info>(count);
423            JsonArray entries = jsonObject.get("entries").asArray();
424            for (JsonValue value : entries) {
425                JsonObject entry = value.asObject();
426                String id = entry.get("id").asString();
427                BoxFolder folder = new BoxFolder(getAPI(), id);
428                pathCollection.add(folder.new Info(entry));
429            }
430
431            return pathCollection;
432        }
433
434        private BoxUser.Info parseUserInfo(JsonObject jsonObject) {
435            String userID = jsonObject.get("id").asString();
436            BoxUser user = new BoxUser(getAPI(), userID);
437            return user.new Info(jsonObject);
438        }
439
440        private List<String> parseTags(JsonArray jsonArray) {
441            List<String> tags = new ArrayList<String>(jsonArray.size());
442            for (JsonValue value : jsonArray) {
443                tags.add(value.asString());
444            }
445
446            return tags;
447        }
448    }
449}