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