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                switch (memberName) {
229                    case "is_reply_comment":
230                        this.isReplyComment = value.asBoolean();
231                        break;
232                    case "message":
233                        this.message = value.asString();
234                        break;
235                    case "tagged_message":
236                        this.taggedMessage = value.asString();
237                        break;
238                    case "created_by":
239                        JsonObject userJSON = value.asObject();
240                        if (this.createdBy == null) {
241                            String userID = userJSON.get("id").asString();
242                            BoxUser user = new BoxUser(getAPI(), userID);
243                            this.createdBy = user.new Info(userJSON);
244                        } else {
245                            this.createdBy.update(userJSON);
246                        }
247                        break;
248                    case "created_at":
249                        this.createdAt = BoxDateFormat.parse(value.asString());
250                        break;
251                    case "item":
252                        this.parseItem(value);
253                        break;
254                    case "modified_by":
255                        userJSON = value.asObject();
256                        if (this.modifiedBy == null) {
257                            String userID = userJSON.get("id").asString();
258                            BoxUser user = new BoxUser(getAPI(), userID);
259                            this.modifiedBy = user.new Info(userJSON);
260                        } else {
261                            this.modifiedBy.update(userJSON);
262                        }
263                        break;
264                    default:
265                        break;
266                }
267            } catch (ParseException e) {
268                assert false : "A ParseException indicates a bug in the SDK.";
269            }
270        }
271
272        private void parseItem(JsonValue value) {
273            JsonObject itemJSON = value.asObject();
274            String itemType = itemJSON.get("type").asString();
275            if (itemType.equals("file")) {
276                this.updateItemAsFile(itemJSON);
277            } else if (itemType.equals("comment")) {
278                this.updateItemAsComment(itemJSON);
279            }
280        }
281
282        private void updateItemAsFile(JsonObject itemJSON) {
283            String itemID = itemJSON.get("id").asString();
284            if (this.item != null && this.item instanceof BoxFile.Info && this.item.getID().equals(itemID)) {
285                this.item.update(itemJSON);
286            } else {
287                BoxFile file = new BoxFile(getAPI(), itemID);
288                this.item = file.new Info(itemJSON);
289            }
290        }
291
292        private void updateItemAsComment(JsonObject itemJSON) {
293            String itemType = itemJSON.get("type").asString();
294            String itemID = itemJSON.get("id").asString();
295            if (this.item != null && this.item instanceof BoxComment.Info && this.item.getID().equals(itemID)) {
296                this.item.update(itemJSON);
297            } else {
298                BoxComment comment = new BoxComment(getAPI(), itemID);
299                this.item = comment.new Info(itemJSON);
300            }
301        }
302    }
303}