| // Copyright (C) 2015 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package com.google.gerrit.client.change; |
| |
| import com.google.gerrit.client.changes.CommentApi; |
| import com.google.gerrit.client.changes.CommentInfo; |
| import com.google.gerrit.client.diff.CommentRange; |
| import com.google.gerrit.client.rpc.GerritCallback; |
| import com.google.gerrit.client.rpc.RestApi; |
| import com.google.gerrit.extensions.client.Side; |
| import com.google.gerrit.reviewdb.client.Change; |
| import com.google.gerrit.reviewdb.client.PatchSet; |
| import com.google.gwt.storage.client.Storage; |
| import com.google.gwt.user.client.Cookies; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| |
| public class LocalComments { |
| private final Change.Id changeId; |
| private final PatchSet.Id psId; |
| private final StorageBackend storage; |
| |
| private static class InlineComment { |
| final PatchSet.Id psId; |
| final CommentInfo commentInfo; |
| |
| InlineComment(PatchSet.Id psId, CommentInfo commentInfo) { |
| this.psId = psId; |
| this.commentInfo = commentInfo; |
| } |
| } |
| |
| private static class StorageBackend { |
| private final Storage storageBackend; |
| |
| StorageBackend() { |
| storageBackend = |
| (Storage.isLocalStorageSupported()) |
| ? Storage.getLocalStorageIfSupported() |
| : Storage.getSessionStorageIfSupported(); |
| } |
| |
| String getItem(String key) { |
| if (storageBackend == null) { |
| return Cookies.getCookie(key); |
| } |
| return storageBackend.getItem(key); |
| } |
| |
| void setItem(String key, String value) { |
| if (storageBackend == null) { |
| Cookies.setCookie(key, value); |
| return; |
| } |
| storageBackend.setItem(key, value); |
| } |
| |
| void removeItem(String key) { |
| if (storageBackend == null) { |
| Cookies.removeCookie(key); |
| return; |
| } |
| storageBackend.removeItem(key); |
| } |
| |
| Collection<String> getKeys() { |
| if (storageBackend == null) { |
| return Cookies.getCookieNames(); |
| } |
| ArrayList<String> result = new ArrayList<>(storageBackend.getLength()); |
| for (int i = 0; i < storageBackend.getLength(); i++) { |
| result.add(storageBackend.key(i)); |
| } |
| return result; |
| } |
| } |
| |
| public LocalComments(Change.Id changeId) { |
| this.changeId = changeId; |
| this.psId = null; |
| this.storage = new StorageBackend(); |
| } |
| |
| public LocalComments(PatchSet.Id psId) { |
| this.changeId = psId.getParentKey(); |
| this.psId = psId; |
| this.storage = new StorageBackend(); |
| } |
| |
| public String getReplyComment() { |
| String comment = storage.getItem(getReplyCommentName()); |
| storage.removeItem(getReplyCommentName()); |
| return comment; |
| } |
| |
| public void setReplyComment(String comment) { |
| storage.setItem(getReplyCommentName(), comment.trim()); |
| } |
| |
| public boolean hasReplyComment() { |
| return storage.getKeys().contains(getReplyCommentName()); |
| } |
| |
| public void removeReplyComment() { |
| if (hasReplyComment()) { |
| storage.removeItem(getReplyCommentName()); |
| } |
| } |
| |
| private String getReplyCommentName() { |
| return "savedReplyComment-" + changeId.toString(); |
| } |
| |
| public static void saveInlineComments() { |
| final StorageBackend storage = new StorageBackend(); |
| for (final String cookie : storage.getKeys()) { |
| if (isInlineComment(cookie)) { |
| InlineComment input = getInlineComment(cookie); |
| if (input.commentInfo.id() == null) { |
| CommentApi.createDraft( |
| input.psId, |
| input.commentInfo, |
| new GerritCallback<CommentInfo>() { |
| @Override |
| public void onSuccess(CommentInfo result) { |
| storage.removeItem(cookie); |
| } |
| }); |
| } else { |
| CommentApi.updateDraft( |
| input.psId, |
| input.commentInfo.id(), |
| input.commentInfo, |
| new GerritCallback<CommentInfo>() { |
| @Override |
| public void onSuccess(CommentInfo result) { |
| storage.removeItem(cookie); |
| } |
| |
| @Override |
| public void onFailure(Throwable caught) { |
| if (RestApi.isNotFound(caught)) { |
| // the draft comment, that was supposed to be updated, |
| // was deleted in the meantime |
| storage.removeItem(cookie); |
| } else { |
| super.onFailure(caught); |
| } |
| } |
| }); |
| } |
| } |
| } |
| } |
| |
| public void setInlineComment(CommentInfo comment) { |
| String name = getInlineCommentName(comment); |
| if (name == null) { |
| // Failed to get the store key -- so we can't continue. |
| return; |
| } |
| storage.setItem(name, comment.message().trim()); |
| } |
| |
| public boolean hasInlineComments() { |
| for (String cookie : storage.getKeys()) { |
| if (isInlineComment(cookie)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private static boolean isInlineComment(String key) { |
| return key.startsWith("patchCommentEdit-") |
| || key.startsWith("patchReply-") |
| || key.startsWith("patchComment-"); |
| } |
| |
| private static InlineComment getInlineComment(String key) { |
| String path; |
| Side side = Side.PARENT; |
| int line = 0; |
| CommentRange range; |
| StorageBackend storage = new StorageBackend(); |
| |
| String[] elements = key.split("-"); |
| int offset = 1; |
| if (key.startsWith("patchReply-") || key.startsWith("patchCommentEdit-")) { |
| offset = 2; |
| } |
| Change.Id changeId = new Change.Id(Integer.parseInt(elements[offset + 0])); |
| PatchSet.Id psId = new PatchSet.Id(changeId, Integer.parseInt(elements[offset + 1])); |
| path = atob(elements[offset + 2]); |
| side = (Side.PARENT.toString().equals(elements[offset + 3])) ? Side.PARENT : Side.REVISION; |
| range = null; |
| if (elements[offset + 4].startsWith("R")) { |
| String rangeStart = elements[offset + 4].substring(1); |
| String rangeEnd = elements[offset + 5]; |
| String[] split = rangeStart.split(","); |
| int sl = Integer.parseInt(split[0]); |
| int sc = Integer.parseInt(split[1]); |
| split = rangeEnd.split(","); |
| int el = Integer.parseInt(split[0]); |
| int ec = Integer.parseInt(split[1]); |
| range = CommentRange.create(sl, sc, el, ec); |
| line = sl; |
| } else { |
| line = Integer.parseInt(elements[offset + 4]); |
| } |
| CommentInfo info = CommentInfo.create(path, side, line, range, false); |
| info.message(storage.getItem(key)); |
| if (key.startsWith("patchReply-")) { |
| info.inReplyTo(elements[1]); |
| } else if (key.startsWith("patchCommentEdit-")) { |
| info.id(elements[1]); |
| } |
| InlineComment inlineComment = new InlineComment(psId, info); |
| return inlineComment; |
| } |
| |
| private String getInlineCommentName(CommentInfo comment) { |
| if (psId == null) { |
| return null; |
| } |
| String result = "patchComment-"; |
| if (comment.id() != null) { |
| result = "patchCommentEdit-" + comment.id() + "-"; |
| } else if (comment.inReplyTo() != null) { |
| result = "patchReply-" + comment.inReplyTo() + "-"; |
| } |
| result += |
| changeId + "-" + psId.getId() + "-" + btoa(comment.path()) + "-" + comment.side() + "-"; |
| if (comment.hasRange()) { |
| result += |
| "R" |
| + comment.range().startLine() |
| + "," |
| + comment.range().startCharacter() |
| + "-" |
| + comment.range().endLine() |
| + "," |
| + comment.range().endCharacter(); |
| } else { |
| result += comment.line(); |
| } |
| return result; |
| } |
| |
| private static native String btoa(String a) /*-{ return btoa(a); }-*/; |
| |
| private static native String atob(String b) /*-{ return atob(b); }-*/; |
| } |