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