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