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