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