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 *
015 * <p>Unless otherwise noted, the methods in this class can throw an unchecked {@link BoxAPIException} (unchecked
016 * meaning that the compiler won't force you to handle it) if an error occurs. If you wish to implement custom error
017 * handling for errors related to the Box REST API, you should capture this exception explicitly.</p>
018 */
019@BoxResourceType("comment")
020public class BoxComment extends BoxResource {
021
022    /**
023     * Add Comment URL Template.
024     */
025    public static final URLTemplate ADD_COMMENT_URL_TEMPLATE = new URLTemplate("comments");
026    /**
027     * Comment URL Template.
028     */
029    public static final URLTemplate COMMENT_URL_TEMPLATE = new URLTemplate("comments/%s");
030
031    private static final Pattern MENTION_REGEX = Pattern.compile("@\\[.+:.+\\]");
032
033    /**
034     * Constructs a BoxComment for a comment with a given ID.
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     * @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     * @return info about this comment.
054     */
055    public Info getInfo() {
056        URL url = COMMENT_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
057        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET");
058        BoxJSONResponse response = (BoxJSONResponse) request.send();
059        JsonObject jsonResponse = JsonObject.readFrom(response.getJSON());
060
061        return new Info(jsonResponse);
062    }
063
064    /**
065     * Changes the message of this comment.
066     * @param  newMessage the new message for this comment.
067     * @return updated info about this comment.
068     */
069    public Info changeMessage(String newMessage) {
070        Info newInfo = new Info();
071        newInfo.setMessage(newMessage);
072
073        URL url = COMMENT_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
074        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT");
075        request.setBody(newInfo.getPendingChanges());
076        BoxJSONResponse response = (BoxJSONResponse) request.send();
077        JsonObject jsonResponse = JsonObject.readFrom(response.getJSON());
078
079        return new Info(jsonResponse);
080    }
081
082    /**
083     * Replies to this comment with another message.
084     * @param  message the message for the reply.
085     * @return info about the newly created reply comment.
086     */
087    public BoxComment.Info reply(String message) {
088        JsonObject itemJSON = new JsonObject();
089        itemJSON.add("type", "comment");
090        itemJSON.add("id", this.getID());
091
092        JsonObject requestJSON = new JsonObject();
093        requestJSON.add("item", itemJSON);
094        if (BoxComment.messageContainsMention(message)) {
095            requestJSON.add("tagged_message", message);
096        } else {
097            requestJSON.add("message", message);
098        }
099
100        URL url = ADD_COMMENT_URL_TEMPLATE.build(this.getAPI().getBaseURL());
101        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "POST");
102        request.setBody(requestJSON.toString());
103        BoxJSONResponse response = (BoxJSONResponse) request.send();
104        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
105
106        BoxComment addedComment = new BoxComment(this.getAPI(), responseJSON.get("id").asString());
107        return addedComment.new Info(responseJSON);
108    }
109
110    /**
111     * Deletes this comment.
112     */
113    public void delete() {
114        URL url = COMMENT_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
115        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "DELETE");
116        BoxAPIResponse response = request.send();
117        response.disconnect();
118    }
119
120    /**
121     * Contains information about a BoxComment.
122     */
123    public class Info extends BoxResource.Info {
124        private boolean isReplyComment;
125        private String message;
126        private String taggedMessage;
127        private BoxUser.Info createdBy;
128        private Date createdAt;
129        private BoxResource.Info item;
130        private BoxUser.Info modifiedBy;
131        private Date modifiedAt;
132
133        /**
134         * Constructs an empty Info object.
135         */
136        public Info() {
137            super();
138        }
139
140        /**
141         * Constructs an Info object by parsing information from a JSON string.
142         * @param  json the JSON string to parse.
143         */
144        public Info(String json) {
145            super(json);
146        }
147
148        /**
149         * Constructs an Info object using an already parsed JSON object.
150         * @param  jsonObject the parsed JSON object.
151         */
152        Info(JsonObject jsonObject) {
153            super(jsonObject);
154        }
155
156        /**
157         * Gets whether or not the comment is a reply to another comment.
158         * @return true if this comment is a reply to another comment; otherwise false.
159         */
160        public boolean getIsReplyComment() {
161            return this.isReplyComment;
162        }
163
164        /**
165         * Gets the comment's message.
166         * @return the comment's message.
167         */
168        public String getMessage() {
169            if (this.taggedMessage != null) {
170                return this.taggedMessage;
171            }
172
173            return this.message;
174        }
175
176        /**
177         * Sets the comment's message. The message can contain @mentions by using the string @[userid:username] anywhere
178         * within the message, where userid and username are the ID and username of the person being mentioned.
179         * @param message the comment's new message.
180         */
181        public void setMessage(String message) {
182            if (messageContainsMention(message)) {
183                this.taggedMessage = message;
184                this.addPendingChange("tagged_message", message);
185                this.removePendingChange("message");
186            } else {
187                this.message = message;
188                this.addPendingChange("message", message);
189                this.removePendingChange("tagged_message");
190            }
191        }
192
193        /**
194         * Gets info about the user who created the comment.
195         * @return info about the user who created the comment.
196         */
197        public BoxUser.Info getCreatedBy() {
198            return this.createdBy;
199        }
200
201        /**
202         * Gets the time the comment was created.
203         * @return the time the comment was created.
204         */
205        public Date getCreatedAt() {
206            return this.createdAt;
207        }
208
209        /**
210         * Gets info about the item this comment is attached to. If the comment is a reply, then the item will be
211         * another BoxComment. Otherwise, the item will be a {@link BoxFile}.
212         * @return the item this comment is attached to.
213         */
214        public BoxResource.Info getItem() {
215            return this.item;
216        }
217
218        /**
219         * Gets info about the user who last modified the comment.
220         * @return info about the user who last modified the comment.
221         */
222        public BoxUser.Info getModifiedBy() {
223            return this.modifiedBy;
224        }
225
226        /**
227         * Gets the time the comment was last modified.
228         * @return the time the comment was last modified.
229         */
230        public Date getModifiedAt() {
231            return this.modifiedAt;
232        }
233
234        @Override
235        public BoxComment getResource() {
236            return BoxComment.this;
237        }
238
239        @Override
240        protected void parseJSONMember(JsonObject.Member member) {
241            super.parseJSONMember(member);
242
243            try {
244                String memberName = member.getName();
245                JsonValue value = member.getValue();
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 (ParseException e) {
284                assert false : "A ParseException indicates a bug in the SDK.";
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}