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}