001package com.box.sdk;
002
003import java.net.URL;
004import java.text.ParseException;
005import java.util.Date;
006import java.util.regex.Pattern;
007
008import com.eclipsesource.json.JsonObject;
009import com.eclipsesource.json.JsonValue;
010
011/**
012 * Represents a comment on a file. Comments can be added directly to a file or they can be created as replies to other
013 * comments.
014 */
015public class BoxComment extends BoxResource {
016    private static final Pattern MENTION_REGEX = Pattern.compile("@\\[.+:.+\\]");
017    private static final URLTemplate ADD_COMMENT_URL_TEMPLATE = new URLTemplate("comments");
018    private static final URLTemplate COMMENT_URL_TEMPLATE = new URLTemplate("comments/%s");
019
020    /**
021     * Constructs a BoxComment for a comment with a given ID.
022     * @param  api the API connection to be used with the comment.
023     * @param  id  the ID of the comment.
024     */
025    public BoxComment(BoxAPIConnection api, String id) {
026        super(api, id);
027    }
028
029    /**
030     * Determines if a comment message contains an @mention.
031     * @param  message the comment message.
032     * @return true if the message contains an @mention; otherwise false.
033     */
034    static boolean messageContainsMention(String message) {
035        return MENTION_REGEX.matcher(message).find();
036    }
037
038    /**
039     * Gets information about this comment.
040     * @return info about this comment.
041     */
042    public Info getInfo() {
043        URL url = COMMENT_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
044        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET");
045        BoxJSONResponse response = (BoxJSONResponse) request.send();
046        JsonObject jsonResponse = JsonObject.readFrom(response.getJSON());
047
048        return new Info(jsonResponse);
049    }
050
051    /**
052     * Changes the message of this comment.
053     * @param  newMessage the new message for this comment.
054     * @return updated info about this comment.
055     */
056    public Info changeMessage(String newMessage) {
057        Info newInfo = new Info();
058        newInfo.setMessage(newMessage);
059
060        URL url = COMMENT_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
061        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT");
062        request.setBody(newInfo.getPendingChanges());
063        BoxJSONResponse response = (BoxJSONResponse) request.send();
064        JsonObject jsonResponse = JsonObject.readFrom(response.getJSON());
065
066        return new Info(jsonResponse);
067    }
068
069    /**
070     * Replies to this comment with another message.
071     * @param  message the message for the reply.
072     * @return info about the newly created reply comment.
073     */
074    public BoxComment.Info reply(String message) {
075        JsonObject itemJSON = new JsonObject();
076        itemJSON.add("type", "comment");
077        itemJSON.add("id", this.getID());
078
079        JsonObject requestJSON = new JsonObject();
080        requestJSON.add("item", itemJSON);
081        if (BoxComment.messageContainsMention(message)) {
082            requestJSON.add("tagged_message", message);
083        } else {
084            requestJSON.add("message", message);
085        }
086
087        URL url = ADD_COMMENT_URL_TEMPLATE.build(this.getAPI().getBaseURL());
088        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "POST");
089        request.setBody(requestJSON.toString());
090        BoxJSONResponse response = (BoxJSONResponse) request.send();
091        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
092
093        BoxComment addedComment = new BoxComment(this.getAPI(), responseJSON.get("id").asString());
094        return addedComment.new Info(responseJSON);
095    }
096
097    /**
098     * Deletes this comment.
099     */
100    public void delete() {
101        URL url = COMMENT_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
102        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "DELETE");
103        BoxAPIResponse response = request.send();
104        response.disconnect();
105    }
106
107    /**
108     * Contains information about a BoxComment.
109     */
110    public class Info extends BoxResource.Info {
111        private boolean isReplyComment;
112        private String message;
113        private String taggedMessage;
114        private BoxUser.Info createdBy;
115        private Date createdAt;
116        private BoxResource.Info item;
117        private BoxUser.Info modifiedBy;
118
119        /**
120         * Constructs an empty Info object.
121         */
122        public Info() {
123            super();
124        }
125
126        /**
127         * Constructs an Info object by parsing information from a JSON string.
128         * @param  json the JSON string to parse.
129         */
130        public Info(String json) {
131            super(json);
132        }
133
134        /**
135         * Constructs an Info object using an already parsed JSON object.
136         * @param  jsonObject the parsed JSON object.
137         */
138        Info(JsonObject jsonObject) {
139            super(jsonObject);
140        }
141
142        /**
143         * Gets whether or not the comment is a reply to another comment.
144         * @return true if this comment is a reply to another comment; otherwise false.
145         */
146        public boolean getIsReplyComment() {
147            return this.isReplyComment;
148        }
149
150        /**
151         * Gets the comment's message.
152         * @return the comment's message.
153         */
154        public String getMessage() {
155            if (this.taggedMessage != null) {
156                return this.taggedMessage;
157            }
158
159            return this.message;
160        }
161
162        /**
163         * Sets the comment's message. The message can contain @mentions by using the string @[userid:username] anywhere
164         * within the message, where userid and username are the ID and username of the person being mentioned.
165         * @param message the comment's new message.
166         */
167        public void setMessage(String message) {
168            if (messageContainsMention(message)) {
169                this.taggedMessage = message;
170                this.addPendingChange("tagged_message", message);
171                this.removePendingChange("message");
172            } else {
173                this.message = message;
174                this.addPendingChange("message", message);
175                this.removePendingChange("tagged_message");
176            }
177        }
178
179        /**
180         * Gets info about the user who created the comment.
181         * @return info about the user who created the comment.
182         */
183        public BoxUser.Info getCreatedBy() {
184            return this.createdBy;
185        }
186
187        /**
188         * Gets the time the comment was created.
189         * @return the time the comment was created.
190         */
191        public Date getCreatedAt() {
192            return this.createdAt;
193        }
194
195        /**
196         * Gets info about the item this comment is attached to. If the comment is a reply, then the item will be
197         * another BoxComment. Otherwise, the item will be a {@link BoxFile}.
198         * @return the item this comment is attached to.
199         */
200        public BoxResource.Info getItem() {
201            return this.item;
202        }
203
204        /**
205         * Gets info about the user who last modified the comment.
206         * @return info about the user who last modified the comment.
207         */
208        public BoxUser.Info getModifiedBy() {
209            return this.modifiedBy;
210        }
211
212        @Override
213        public BoxComment getResource() {
214            return BoxComment.this;
215        }
216
217        @Override
218        protected void parseJSONMember(JsonObject.Member member) {
219            super.parseJSONMember(member);
220
221            try {
222                String memberName = member.getName();
223                JsonValue value = member.getValue();
224                switch (memberName) {
225                    case "is_reply_comment":
226                        this.isReplyComment = value.asBoolean();
227                        break;
228                    case "message":
229                        this.message = value.asString();
230                        break;
231                    case "tagged_message":
232                        this.taggedMessage = value.asString();
233                        break;
234                    case "created_by":
235                        JsonObject userJSON = value.asObject();
236                        if (this.createdBy == null) {
237                            String userID = userJSON.get("id").asString();
238                            BoxUser user = new BoxUser(getAPI(), userID);
239                            this.createdBy = user.new Info(userJSON);
240                        } else {
241                            this.createdBy.update(userJSON);
242                        }
243                        break;
244                    case "created_at":
245                        this.createdAt = BoxDateFormat.parse(value.asString());
246                        break;
247                    case "item":
248                        this.parseItem(value);
249                        break;
250                    case "modified_by":
251                        userJSON = value.asObject();
252                        if (this.modifiedBy == null) {
253                            String userID = userJSON.get("id").asString();
254                            BoxUser user = new BoxUser(getAPI(), userID);
255                            this.modifiedBy = user.new Info(userJSON);
256                        } else {
257                            this.modifiedBy.update(userJSON);
258                        }
259                        break;
260                    default:
261                        break;
262                }
263            } catch (ParseException e) {
264                assert false : "A ParseException indicates a bug in the SDK.";
265            }
266        }
267
268        private void parseItem(JsonValue value) {
269            JsonObject itemJSON = value.asObject();
270            String itemType = itemJSON.get("type").asString();
271            if (itemType.equals("file")) {
272                this.updateItemAsFile(itemJSON);
273            } else if (itemType.equals("comment")) {
274                this.updateItemAsComment(itemJSON);
275            }
276        }
277
278        private void updateItemAsFile(JsonObject itemJSON) {
279            String itemID = itemJSON.get("id").asString();
280            if (this.item != null && this.item instanceof BoxFile.Info && this.item.getID().equals(itemID)) {
281                this.item.update(itemJSON);
282            } else {
283                BoxFile file = new BoxFile(getAPI(), itemID);
284                this.item = file.new Info(itemJSON);
285            }
286        }
287
288        private void updateItemAsComment(JsonObject itemJSON) {
289            String itemType = itemJSON.get("type").asString();
290            String itemID = itemJSON.get("id").asString();
291            if (this.item != null && this.item instanceof BoxComment.Info && this.item.getID().equals(itemID)) {
292                this.item.update(itemJSON);
293            } else {
294                BoxComment comment = new BoxComment(getAPI(), itemID);
295                this.item = comment.new Info(itemJSON);
296            }
297        }
298    }
299}