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        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "GET");
060        try (BoxJSONResponse response = request.send()) {
061            JsonObject jsonResponse = Json.parse(response.getJSON()).asObject();
062
063            return new Info(jsonResponse);
064        }
065    }
066
067    /**
068     * Changes the message of this comment.
069     *
070     * @param newMessage the new message for this comment.
071     * @return updated info about this comment.
072     */
073    public Info changeMessage(String newMessage) {
074        Info newInfo = new Info();
075        newInfo.setMessage(newMessage);
076
077        URL url = COMMENT_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
078        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT");
079        request.setBody(newInfo.getPendingChanges());
080        try (BoxJSONResponse response = request.send()) {
081            JsonObject jsonResponse = Json.parse(response.getJSON()).asObject();
082
083            return new Info(jsonResponse);
084        }
085    }
086
087    /**
088     * Replies to this comment with another message.
089     *
090     * @param message the message for the reply.
091     * @return info about the newly created reply comment.
092     */
093    public BoxComment.Info reply(String message) {
094        JsonObject itemJSON = new JsonObject();
095        itemJSON.add("type", "comment");
096        itemJSON.add("id", this.getID());
097
098        JsonObject requestJSON = new JsonObject();
099        requestJSON.add("item", itemJSON);
100        if (BoxComment.messageContainsMention(message)) {
101            requestJSON.add("tagged_message", message);
102        } else {
103            requestJSON.add("message", message);
104        }
105
106        URL url = ADD_COMMENT_URL_TEMPLATE.build(this.getAPI().getBaseURL());
107        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "POST");
108        request.setBody(requestJSON.toString());
109        try (BoxJSONResponse response = request.send()) {
110            JsonObject responseJSON = Json.parse(response.getJSON()).asObject();
111
112            BoxComment addedComment = new BoxComment(this.getAPI(), responseJSON.get("id").asString());
113            return addedComment.new Info(responseJSON);
114        }
115    }
116
117    /**
118     * Deletes this comment.
119     */
120    public void delete() {
121        URL url = COMMENT_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
122        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "DELETE");
123        request.send().close();
124    }
125
126    /**
127     * Contains information about a BoxComment.
128     */
129    public class Info extends BoxResource.Info {
130        private boolean isReplyComment;
131        private String message;
132        private String taggedMessage;
133        private BoxUser.Info createdBy;
134        private Date createdAt;
135        private BoxResource.Info item;
136        private BoxUser.Info modifiedBy;
137        private Date modifiedAt;
138
139        /**
140         * Constructs an empty Info object.
141         */
142        public Info() {
143            super();
144        }
145
146        /**
147         * Constructs an Info object by parsing information from a JSON string.
148         *
149         * @param json the JSON string to parse.
150         */
151        public Info(String json) {
152            super(json);
153        }
154
155        /**
156         * Constructs an Info object using an already parsed JSON object.
157         *
158         * @param jsonObject the parsed JSON object.
159         */
160        Info(JsonObject jsonObject) {
161            super(jsonObject);
162        }
163
164        /**
165         * Gets whether or not the comment is a reply to another comment.
166         *
167         * @return true if this comment is a reply to another comment; otherwise false.
168         */
169        public boolean getIsReplyComment() {
170            return this.isReplyComment;
171        }
172
173        /**
174         * Gets the comment's message.
175         *
176         * @return the comment's message.
177         */
178        public String getMessage() {
179            if (this.taggedMessage != null) {
180                return this.taggedMessage;
181            }
182
183            return this.message;
184        }
185
186        /**
187         * Sets the comment's message. The message can contain @mentions by using the string @[userid:username] anywhere
188         * within the message, where userid and username are the ID and username of the person being mentioned.
189         *
190         * @param message the comment's new message.
191         */
192        public void setMessage(String message) {
193            if (messageContainsMention(message)) {
194                this.taggedMessage = message;
195                this.addPendingChange("tagged_message", message);
196                this.removePendingChange("message");
197            } else {
198                this.message = message;
199                this.addPendingChange("message", message);
200                this.removePendingChange("tagged_message");
201            }
202        }
203
204        /**
205         * Gets info about the user who created the comment.
206         *
207         * @return info about the user who created the comment.
208         */
209        public BoxUser.Info getCreatedBy() {
210            return this.createdBy;
211        }
212
213        /**
214         * Gets the time the comment was created.
215         *
216         * @return the time the comment was created.
217         */
218        public Date getCreatedAt() {
219            return this.createdAt;
220        }
221
222        /**
223         * Gets info about the item this comment is attached to. If the comment is a reply, then the item will be
224         * another BoxComment. Otherwise, the item will be a {@link BoxFile}.
225         *
226         * @return the item this comment is attached to.
227         */
228        public BoxResource.Info getItem() {
229            return this.item;
230        }
231
232        /**
233         * Gets info about the user who last modified the comment.
234         *
235         * @return info about the user who last modified the comment.
236         */
237        public BoxUser.Info getModifiedBy() {
238            return this.modifiedBy;
239        }
240
241        /**
242         * Gets the time the comment was last modified.
243         *
244         * @return the time the comment was last modified.
245         */
246        public Date getModifiedAt() {
247            return this.modifiedAt;
248        }
249
250        @Override
251        public BoxComment getResource() {
252            return BoxComment.this;
253        }
254
255        @Override
256        protected void parseJSONMember(JsonObject.Member member) {
257            super.parseJSONMember(member);
258            String memberName = member.getName();
259            JsonValue value = member.getValue();
260
261            try {
262
263                if (memberName.equals("is_reply_comment")) {
264                    this.isReplyComment = value.asBoolean();
265
266                } else if (memberName.equals("message")) {
267                    this.message = value.asString();
268
269                } else if (memberName.equals("tagged_message")) {
270                    this.taggedMessage = value.asString();
271
272                } else if (memberName.equals("created_by")) {
273                    JsonObject userJSON = value.asObject();
274                    if (this.createdBy == null) {
275                        String userID = userJSON.get("id").asString();
276                        BoxUser user = new BoxUser(getAPI(), userID);
277                        this.createdBy = user.new Info(userJSON);
278                    } else {
279                        this.createdBy.update(userJSON);
280                    }
281
282                } else if (memberName.equals("created_at")) {
283                    this.createdAt = BoxDateFormat.parse(value.asString());
284
285                } else if (memberName.equals("item")) {
286                    this.parseItem(value);
287
288                } else if (memberName.equals("modified_by")) {
289                    JsonObject userJSON = value.asObject();
290                    if (this.modifiedBy == null) {
291                        String userID = userJSON.get("id").asString();
292                        BoxUser user = new BoxUser(getAPI(), userID);
293                        this.modifiedBy = user.new Info(userJSON);
294                    } else {
295                        this.modifiedBy.update(userJSON);
296                    }
297                } else if (memberName.equals("modified_at")) {
298                    this.modifiedAt = BoxDateFormat.parse(value.asString());
299                }
300            } catch (Exception e) {
301                throw new BoxDeserializationException(memberName, value.toString(), e);
302            }
303        }
304
305        private void parseItem(JsonValue value) {
306            JsonObject itemJSON = value.asObject();
307            String itemType = itemJSON.get("type").asString();
308            if (itemType.equals("file")) {
309                this.updateItemAsFile(itemJSON);
310            } else if (itemType.equals("comment")) {
311                this.updateItemAsComment(itemJSON);
312            }
313        }
314
315        private void updateItemAsFile(JsonObject itemJSON) {
316            String itemID = itemJSON.get("id").asString();
317            if (this.item != null && this.item instanceof BoxFile.Info && this.item.getID().equals(itemID)) {
318                this.item.update(itemJSON);
319            } else {
320                BoxFile file = new BoxFile(getAPI(), itemID);
321                this.item = file.new Info(itemJSON);
322            }
323        }
324
325        private void updateItemAsComment(JsonObject itemJSON) {
326            String itemID = itemJSON.get("id").asString();
327            if (this.item != null && this.item instanceof BoxComment.Info && this.item.getID().equals(itemID)) {
328                this.item.update(itemJSON);
329            } else {
330                BoxComment comment = new BoxComment(getAPI(), itemID);
331                this.item = comment.new Info(itemJSON);
332            }
333        }
334    }
335}