001package com.box.sdk;
002
003import com.eclipsesource.json.Json;
004import com.eclipsesource.json.JsonObject;
005import com.eclipsesource.json.JsonValue;
006import java.net.URL;
007import java.util.Date;
008import java.util.regex.Pattern;
009
010/**
011 * Represents a comment on a file. Comments can be added directly to a file or they can be created
012 * as replies to other comments.
013 *
014 * <p>Unless otherwise noted, the methods in this class can throw an unchecked {@link
015 * BoxAPIException} (unchecked meaning that the compiler won't force you to handle it) if an error
016 * occurs. If you wish to implement custom error handling for errors related to the Box REST API,
017 * you should capture this exception explicitly.
018 */
019@BoxResourceType("comment")
020public class BoxComment extends BoxResource {
021
022  /** Add Comment URL Template. */
023  public static final URLTemplate ADD_COMMENT_URL_TEMPLATE = new URLTemplate("comments");
024  /** Comment URL Template. */
025  public static final URLTemplate COMMENT_URL_TEMPLATE = new URLTemplate("comments/%s");
026
027  private static final Pattern MENTION_REGEX = Pattern.compile("@\\[.+:.+\\]");
028
029  /**
030   * Constructs a BoxComment for a comment with a given ID.
031   *
032   * @param api the API connection to be used with the comment.
033   * @param id the ID of the comment.
034   */
035  public BoxComment(BoxAPIConnection api, String id) {
036    super(api, id);
037  }
038
039  /**
040   * Determines if a comment message contains an @mention.
041   *
042   * @param message the comment message.
043   * @return true if the message contains an @mention; otherwise false.
044   */
045  static boolean messageContainsMention(String message) {
046    return MENTION_REGEX.matcher(message).find();
047  }
048
049  /**
050   * Gets information about this comment.
051   *
052   * @return info about this comment.
053   */
054  public Info getInfo() {
055    URL url = COMMENT_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
056    BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "GET");
057    try (BoxJSONResponse response = request.send()) {
058      JsonObject jsonResponse = Json.parse(response.getJSON()).asObject();
059
060      return new Info(jsonResponse);
061    }
062  }
063
064  /**
065   * Changes the message of this comment.
066   *
067   * @param newMessage the new message for this comment.
068   * @return updated info about this comment.
069   */
070  public Info changeMessage(String newMessage) {
071    Info newInfo = new Info();
072    newInfo.setMessage(newMessage);
073
074    URL url = COMMENT_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
075    BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT");
076    request.setBody(newInfo.getPendingChanges());
077    try (BoxJSONResponse response = request.send()) {
078      JsonObject jsonResponse = Json.parse(response.getJSON()).asObject();
079
080      return new Info(jsonResponse);
081    }
082  }
083
084  /**
085   * Replies to this comment with another message.
086   *
087   * @param message the message for the reply.
088   * @return info about the newly created reply comment.
089   */
090  public BoxComment.Info reply(String message) {
091    JsonObject itemJSON = new JsonObject();
092    itemJSON.add("type", "comment");
093    itemJSON.add("id", this.getID());
094
095    JsonObject requestJSON = new JsonObject();
096    requestJSON.add("item", itemJSON);
097    if (BoxComment.messageContainsMention(message)) {
098      requestJSON.add("tagged_message", message);
099    } else {
100      requestJSON.add("message", message);
101    }
102
103    URL url = ADD_COMMENT_URL_TEMPLATE.build(this.getAPI().getBaseURL());
104    BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "POST");
105    request.setBody(requestJSON.toString());
106    try (BoxJSONResponse response = request.send()) {
107      JsonObject responseJSON = Json.parse(response.getJSON()).asObject();
108
109      BoxComment addedComment = new BoxComment(this.getAPI(), responseJSON.get("id").asString());
110      return addedComment.new Info(responseJSON);
111    }
112  }
113
114  /** Deletes this comment. */
115  public void delete() {
116    URL url = COMMENT_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
117    BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "DELETE");
118    request.send().close();
119  }
120
121  /** Contains information about a BoxComment. */
122  public class Info extends BoxResource.Info {
123    private boolean isReplyComment;
124    private String message;
125    private String taggedMessage;
126    private BoxUser.Info createdBy;
127    private Date createdAt;
128    private BoxResource.Info item;
129    private BoxUser.Info modifiedBy;
130    private Date modifiedAt;
131
132    /** Constructs an empty Info object. */
133    public Info() {
134      super();
135    }
136
137    /**
138     * Constructs an Info object by parsing information from a JSON string.
139     *
140     * @param json the JSON string to parse.
141     */
142    public Info(String json) {
143      super(json);
144    }
145
146    /**
147     * Constructs an Info object using an already parsed JSON object.
148     *
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     *
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     *
167     * @return the comment's message.
168     */
169    public String getMessage() {
170      if (this.taggedMessage != null) {
171        return this.taggedMessage;
172      }
173
174      return this.message;
175    }
176
177    /**
178     * Sets the comment's message. The message can contain @mentions by using the
179     * string @[userid:username] anywhere within the message, where userid and username are the ID
180     * and username of the person being mentioned.
181     *
182     * @param message the comment's new message.
183     */
184    public void setMessage(String message) {
185      if (messageContainsMention(message)) {
186        this.taggedMessage = message;
187        this.addPendingChange("tagged_message", message);
188        this.removePendingChange("message");
189      } else {
190        this.message = message;
191        this.addPendingChange("message", message);
192        this.removePendingChange("tagged_message");
193      }
194    }
195
196    /**
197     * Gets info about the user who created the comment.
198     *
199     * @return info about the user who created the comment.
200     */
201    public BoxUser.Info getCreatedBy() {
202      return this.createdBy;
203    }
204
205    /**
206     * Gets the time the comment was created.
207     *
208     * @return the time the comment was created.
209     */
210    public Date getCreatedAt() {
211      return this.createdAt;
212    }
213
214    /**
215     * Gets info about the item this comment is attached to. If the comment is a reply, then the
216     * item will be another BoxComment. Otherwise, the item will be a {@link BoxFile}.
217     *
218     * @return the item this comment is attached to.
219     */
220    public BoxResource.Info getItem() {
221      return this.item;
222    }
223
224    /**
225     * Gets info about the user who last modified the comment.
226     *
227     * @return info about the user who last modified the comment.
228     */
229    public BoxUser.Info getModifiedBy() {
230      return this.modifiedBy;
231    }
232
233    /**
234     * Gets the time the comment was last modified.
235     *
236     * @return the time the comment was last modified.
237     */
238    public Date getModifiedAt() {
239      return this.modifiedAt;
240    }
241
242    @Override
243    public BoxComment getResource() {
244      return BoxComment.this;
245    }
246
247    @Override
248    protected void parseJSONMember(JsonObject.Member member) {
249      super.parseJSONMember(member);
250      String memberName = member.getName();
251      JsonValue value = member.getValue();
252
253      try {
254
255        if (memberName.equals("is_reply_comment")) {
256          this.isReplyComment = value.asBoolean();
257
258        } else if (memberName.equals("message")) {
259          this.message = value.asString();
260
261        } else if (memberName.equals("tagged_message")) {
262          this.taggedMessage = value.asString();
263
264        } else if (memberName.equals("created_by")) {
265          JsonObject userJSON = value.asObject();
266          if (this.createdBy == null) {
267            String userID = userJSON.get("id").asString();
268            BoxUser user = new BoxUser(getAPI(), userID);
269            this.createdBy = user.new Info(userJSON);
270          } else {
271            this.createdBy.update(userJSON);
272          }
273
274        } else if (memberName.equals("created_at")) {
275          this.createdAt = BoxDateFormat.parse(value.asString());
276
277        } else if (memberName.equals("item")) {
278          this.parseItem(value);
279
280        } else if (memberName.equals("modified_by")) {
281          JsonObject userJSON = value.asObject();
282          if (this.modifiedBy == null) {
283            String userID = userJSON.get("id").asString();
284            BoxUser user = new BoxUser(getAPI(), userID);
285            this.modifiedBy = user.new Info(userJSON);
286          } else {
287            this.modifiedBy.update(userJSON);
288          }
289        } else if (memberName.equals("modified_at")) {
290          this.modifiedAt = BoxDateFormat.parse(value.asString());
291        }
292      } catch (Exception e) {
293        throw new BoxDeserializationException(memberName, value.toString(), e);
294      }
295    }
296
297    private void parseItem(JsonValue value) {
298      JsonObject itemJSON = value.asObject();
299      String itemType = itemJSON.get("type").asString();
300      if (itemType.equals("file")) {
301        this.updateItemAsFile(itemJSON);
302      } else if (itemType.equals("comment")) {
303        this.updateItemAsComment(itemJSON);
304      }
305    }
306
307    private void updateItemAsFile(JsonObject itemJSON) {
308      String itemID = itemJSON.get("id").asString();
309      if (this.item != null
310          && this.item instanceof BoxFile.Info
311          && this.item.getID().equals(itemID)) {
312        this.item.update(itemJSON);
313      } else {
314        BoxFile file = new BoxFile(getAPI(), itemID);
315        this.item = file.new Info(itemJSON);
316      }
317    }
318
319    private void updateItemAsComment(JsonObject itemJSON) {
320      String itemID = itemJSON.get("id").asString();
321      if (this.item != null
322          && this.item instanceof BoxComment.Info
323          && this.item.getID().equals(itemID)) {
324        this.item.update(itemJSON);
325      } else {
326        BoxComment comment = new BoxComment(getAPI(), itemID);
327        this.item = comment.new Info(itemJSON);
328      }
329    }
330  }
331}