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