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