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