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