blob: 733da0a9bbae25d90b4aa9a344e1c102806f7f9f [file] [log] [blame]
Dave Borowitz9de65952012-08-13 16:09:45 -07001// Copyright 2012 Google Inc. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package com.google.gitiles;
16
Dave Borowitz9de65952012-08-13 16:09:45 -070017import static com.google.common.base.Preconditions.checkState;
Dave Borowitzd6184a62013-01-10 11:53:54 -080018import static org.eclipse.jgit.diff.DiffEntry.ChangeType.COPY;
19import static org.eclipse.jgit.diff.DiffEntry.ChangeType.DELETE;
20import static org.eclipse.jgit.diff.DiffEntry.ChangeType.RENAME;
21
Dave Borowitz9de65952012-08-13 16:09:45 -070022import com.google.common.collect.ImmutableMap;
23import com.google.common.collect.ImmutableSet;
24import com.google.common.collect.Lists;
25import com.google.common.collect.Maps;
Dave Borowitz75a78322014-03-16 12:13:08 -070026import com.google.common.collect.Sets;
Dave Borowitz01551272014-03-16 13:43:16 -070027import com.google.gitiles.CommitData.DiffList;
28import com.google.gitiles.CommitData.Field;
Dave Borowitz9de65952012-08-13 16:09:45 -070029import com.google.template.soy.data.restricted.NullData;
30
31import org.eclipse.jgit.diff.DiffEntry;
32import org.eclipse.jgit.diff.DiffEntry.ChangeType;
Dave Borowitz9de65952012-08-13 16:09:45 -070033import org.eclipse.jgit.lib.Constants;
Dave Borowitz9de65952012-08-13 16:09:45 -070034import org.eclipse.jgit.lib.PersonIdent;
35import org.eclipse.jgit.lib.Ref;
Dave Borowitz9de65952012-08-13 16:09:45 -070036import org.eclipse.jgit.revwalk.RevCommit;
37import org.eclipse.jgit.revwalk.RevWalk;
Dave Borowitz9de65952012-08-13 16:09:45 -070038import org.eclipse.jgit.util.RelativeDateFormatter;
Dave Borowitz9de65952012-08-13 16:09:45 -070039
40import java.io.IOException;
Dave Borowitz9de65952012-08-13 16:09:45 -070041import java.util.List;
42import java.util.Map;
43import java.util.Set;
44
Dave Borowitz01551272014-03-16 13:43:16 -070045import javax.annotation.Nullable;
Dave Borowitz9de65952012-08-13 16:09:45 -070046import javax.servlet.http.HttpServletRequest;
47
48/** Soy data converter for git commits. */
Dave Borowitz6ed598f2014-04-17 10:09:50 -070049public class CommitSoyData {
Dave Borowitz01551272014-03-16 13:43:16 -070050 static final ImmutableSet<Field> DEFAULT_FIELDS = Sets.immutableEnumSet(Field.AUTHOR,
51 Field.COMMITTER, Field.SHA, Field.TREE, Field.TREE_URL, Field.PARENTS, Field.MESSAGE,
52 Field.LOG_URL, Field.ARCHIVE_URL, Field.ARCHIVE_TYPE);
Dave Borowitz9de65952012-08-13 16:09:45 -070053
Dave Borowitzf03fcea2014-04-21 17:20:33 -070054 private static final ImmutableSet<Field> NESTED_FIELDS = Sets.immutableEnumSet(
55 Field.PARENT_BLAME_URL);
56
Dave Borowitz3b086a72013-07-02 15:03:03 -070057 private Linkifier linkifier;
Dave Borowitz3b086a72013-07-02 15:03:03 -070058 private RevWalk walk;
Dave Borowitzc8a15682013-07-02 14:33:08 -070059 private ArchiveFormat archiveFormat;
Dave Borowitz9de65952012-08-13 16:09:45 -070060
Dave Borowitz01551272014-03-16 13:43:16 -070061 CommitSoyData setLinkifier(@Nullable Linkifier linkifier) {
62 this.linkifier = linkifier;
Dave Borowitz3b086a72013-07-02 15:03:03 -070063 return this;
Dave Borowitz9de65952012-08-13 16:09:45 -070064 }
65
Dave Borowitz01551272014-03-16 13:43:16 -070066 CommitSoyData setRevWalk(@Nullable RevWalk walk) {
67 this.walk = walk;
Dave Borowitz3b086a72013-07-02 15:03:03 -070068 return this;
Dave Borowitz9de65952012-08-13 16:09:45 -070069 }
70
Dave Borowitz01551272014-03-16 13:43:16 -070071 CommitSoyData setArchiveFormat(@Nullable ArchiveFormat archiveFormat) {
72 this.archiveFormat = archiveFormat;
Dave Borowitzc8a15682013-07-02 14:33:08 -070073 return this;
74 }
75
Dave Borowitz01551272014-03-16 13:43:16 -070076 Map<String, Object> toSoyData(HttpServletRequest req, RevCommit c, Set<Field> fs,
Dave Borowitz97a70312014-04-28 15:50:23 -070077 DateFormatter df) throws IOException {
Dave Borowitz01551272014-03-16 13:43:16 -070078 GitilesView view = ViewFilter.getView(req);
79 CommitData cd = new CommitData.Builder()
80 .setRevWalk(walk)
81 .setArchiveFormat(archiveFormat)
82 .build(req, c, fs);
Dave Borowitz3b086a72013-07-02 15:03:03 -070083
Dave Borowitz01551272014-03-16 13:43:16 -070084 Map<String, Object> data = Maps.newHashMapWithExpectedSize(fs.size());
85 if (cd.author != null) {
86 data.put("author", toSoyData(cd.author, df));
Dave Borowitz9de65952012-08-13 16:09:45 -070087 }
Dave Borowitz01551272014-03-16 13:43:16 -070088 if (cd.committer != null) {
89 data.put("committer", toSoyData(cd.committer, df));
Dave Borowitz9de65952012-08-13 16:09:45 -070090 }
Dave Borowitz01551272014-03-16 13:43:16 -070091 if (cd.sha != null) {
92 data.put("sha", cd.sha.name());
Dave Borowitz9de65952012-08-13 16:09:45 -070093 }
Dave Borowitz01551272014-03-16 13:43:16 -070094 if (cd.abbrev != null) {
95 data.put("abbrevSha", cd.abbrev.name());
Dave Borowitz9de65952012-08-13 16:09:45 -070096 }
Dave Borowitz01551272014-03-16 13:43:16 -070097 if (cd.url != null) {
98 data.put("url", cd.url);
Dave Borowitz9de65952012-08-13 16:09:45 -070099 }
Dave Borowitz01551272014-03-16 13:43:16 -0700100 if (cd.logUrl != null) {
101 data.put("logUrl", cd.logUrl);
Dave Borowitzc222cce2013-06-19 10:47:06 -0700102 }
Dave Borowitz01551272014-03-16 13:43:16 -0700103 if (cd.archiveUrl != null) {
104 data.put("archiveUrl", cd.archiveUrl);
Dave Borowitzc8a15682013-07-02 14:33:08 -0700105 }
Dave Borowitz01551272014-03-16 13:43:16 -0700106 if (cd.archiveType != null) {
107 data.put("archiveType", cd.archiveType.getShortName());
Dave Borowitz9de65952012-08-13 16:09:45 -0700108 }
Dave Borowitz01551272014-03-16 13:43:16 -0700109 if (cd.tree != null) {
110 data.put("tree", cd.tree.name());
Dave Borowitz9de65952012-08-13 16:09:45 -0700111 }
Dave Borowitz01551272014-03-16 13:43:16 -0700112 if (cd.treeUrl != null) {
113 data.put("treeUrl", cd.treeUrl);
Dave Borowitz9de65952012-08-13 16:09:45 -0700114 }
Dave Borowitz01551272014-03-16 13:43:16 -0700115 if (cd.parents != null) {
Dave Borowitzf03fcea2014-04-21 17:20:33 -0700116 data.put("parents", toSoyData(view, fs, cd.parents));
Dave Borowitz9de65952012-08-13 16:09:45 -0700117 }
Dave Borowitz01551272014-03-16 13:43:16 -0700118 if (cd.shortMessage != null) {
119 data.put("shortMessage", cd.shortMessage);
Dave Borowitz9de65952012-08-13 16:09:45 -0700120 }
Dave Borowitz01551272014-03-16 13:43:16 -0700121 if (cd.branches != null) {
122 data.put("branches", toSoyData(view, cd.branches, Constants.R_HEADS));
Dave Borowitz9de65952012-08-13 16:09:45 -0700123 }
Dave Borowitz01551272014-03-16 13:43:16 -0700124 if (cd.tags != null) {
125 data.put("tags", toSoyData(view, cd.tags, Constants.R_TAGS));
Dave Borowitz9de65952012-08-13 16:09:45 -0700126 }
Dave Borowitz01551272014-03-16 13:43:16 -0700127 if (cd.message != null) {
Dave Borowitz9de65952012-08-13 16:09:45 -0700128 if (linkifier != null) {
Dave Borowitz01551272014-03-16 13:43:16 -0700129 data.put("message", linkifier.linkify(req, cd.message));
Dave Borowitz9de65952012-08-13 16:09:45 -0700130 } else {
Dave Borowitz01551272014-03-16 13:43:16 -0700131 data.put("message", cd.message);
Dave Borowitz9de65952012-08-13 16:09:45 -0700132 }
133 }
Dave Borowitz01551272014-03-16 13:43:16 -0700134 if (cd.diffEntries != null) {
135 data.put("diffTree", toSoyData(view, cd.diffEntries));
Dave Borowitz9de65952012-08-13 16:09:45 -0700136 }
Dave Borowitzf03fcea2014-04-21 17:20:33 -0700137 checkState(Sets.difference(fs, NESTED_FIELDS).size() == data.size(),
138 "bad commit data fields: %s != %s", fs, data.keySet());
Michael Moss92bb12f2014-05-09 10:55:35 -0700139 return data;
Dave Borowitz9de65952012-08-13 16:09:45 -0700140 }
141
Dave Borowitz01551272014-03-16 13:43:16 -0700142 Map<String, Object> toSoyData(HttpServletRequest req, RevCommit commit,
Dave Borowitz97a70312014-04-28 15:50:23 -0700143 DateFormatter df) throws IOException {
Dave Borowitz01551272014-03-16 13:43:16 -0700144 return toSoyData(req, commit, DEFAULT_FIELDS, df);
Dave Borowitz9de65952012-08-13 16:09:45 -0700145 }
146
147 // TODO(dborowitz): Extract this.
Dave Borowitz97a70312014-04-28 15:50:23 -0700148 public static Map<String, String> toSoyData(PersonIdent ident, DateFormatter df) {
Dave Borowitz9de65952012-08-13 16:09:45 -0700149 return ImmutableMap.of(
150 "name", ident.getName(),
151 "email", ident.getEmailAddress(),
Dave Borowitz97a70312014-04-28 15:50:23 -0700152 "time", df.format(ident),
Dave Borowitz9de65952012-08-13 16:09:45 -0700153 // TODO(dborowitz): Switch from relative to absolute at some threshold.
154 "relativeTime", RelativeDateFormatter.format(ident.getWhen()));
155 }
156
Dave Borowitzf03fcea2014-04-21 17:20:33 -0700157 private List<Map<String, String>> toSoyData(GitilesView view, Set<Field> fs,
158 List<RevCommit> parents) {
Dave Borowitz01551272014-03-16 13:43:16 -0700159 List<Map<String, String>> result = Lists.newArrayListWithCapacity(parents.size());
Dave Borowitz9de65952012-08-13 16:09:45 -0700160 int i = 1;
161 // TODO(dborowitz): Render something slightly different when we're actively
162 // viewing a diff against one of the parents.
163 for (RevCommit parent : parents) {
164 String name = parent.name();
Dave Borowitz359ad6d2014-04-21 16:07:59 -0700165 // Clear path on parent diff view, since this parent may not have a diff
166 // for the path in question.
Dave Borowitzdd3c3d92013-03-11 16:38:41 -0700167 GitilesView.Builder diff = GitilesView.diff().copyFrom(view).setPathPart("");
Dave Borowitz9de65952012-08-13 16:09:45 -0700168 String parentName;
Dave Borowitz01551272014-03-16 13:43:16 -0700169 if (parents.size() == 1) {
Dave Borowitz9de65952012-08-13 16:09:45 -0700170 parentName = view.getRevision().getName() + "^";
171 } else {
172 parentName = view.getRevision().getName() + "^" + (i++);
173 }
Dave Borowitz8d11fb72014-09-23 10:27:31 -0700174 diff.setOldRevision(parentName, parent);
175
Dave Borowitzf03fcea2014-04-21 17:20:33 -0700176 Map<String, String> e = Maps.newHashMapWithExpectedSize(4);
177 e.put("sha", name);
178 e.put("url", GitilesView.revision()
179 .copyFrom(view)
180 .setRevision(parentName, parent)
181 .toUrl());
Dave Borowitz8d11fb72014-09-23 10:27:31 -0700182 e.put("diffUrl", diff.toUrl());
Dave Borowitzf03fcea2014-04-21 17:20:33 -0700183 if (fs.contains(Field.PARENT_BLAME_URL)) {
184 // Assumes caller has ensured path is a file.
185 e.put("blameUrl", GitilesView.blame()
186 .copyFrom(view)
187 .setRevision(Revision.peeled(parentName, parent))
188 .toUrl());
189 }
190 result.add(e);
Dave Borowitz9de65952012-08-13 16:09:45 -0700191 }
192 return result;
193 }
194
Dave Borowitz01551272014-03-16 13:43:16 -0700195 private static Object toSoyData(GitilesView view, DiffList dl) {
196 if (dl.oldRevision == null) {
197 return NullData.INSTANCE;
198 }
Dave Borowitz2298cef2014-09-23 10:29:46 -0700199 GitilesView.Builder diffUrl = GitilesView.diff()
200 .copyFrom(view)
201 .setOldRevision(dl.oldRevision)
202 .setRevision(dl.revision)
Dave Borowitzdd3c3d92013-03-11 16:38:41 -0700203 .setPathPart("");
Dave Borowitz9de65952012-08-13 16:09:45 -0700204
Dave Borowitz01551272014-03-16 13:43:16 -0700205 List<Object> result = Lists.newArrayListWithCapacity(dl.entries.size());
206 for (DiffEntry e : dl.entries) {
207 Map<String, Object> entry = Maps.newHashMapWithExpectedSize(5);
208 ChangeType type = e.getChangeType();
209 if (type != DELETE) {
210 entry.put("path", e.getNewPath());
211 entry.put("url", GitilesView.path()
212 .copyFrom(view)
Dave Borowitz2298cef2014-09-23 10:29:46 -0700213 .setRevision(dl.revision)
Dave Borowitz01551272014-03-16 13:43:16 -0700214 .setPathPart(e.getNewPath())
215 .toUrl());
216 } else {
217 entry.put("path", e.getOldPath());
218 entry.put("url", GitilesView.path()
219 .copyFrom(view)
220 .setRevision(dl.oldRevision)
221 .setPathPart(e.getOldPath())
222 .toUrl());
Dave Borowitz9de65952012-08-13 16:09:45 -0700223 }
Dave Borowitz01551272014-03-16 13:43:16 -0700224 entry.put("diffUrl", diffUrl.setAnchor("F" + result.size()).toUrl());
225 entry.put("changeType", e.getChangeType().toString());
226 if (type == COPY || type == RENAME) {
227 entry.put("oldPath", e.getOldPath());
228 }
229 result.add(entry);
Dave Borowitz9de65952012-08-13 16:09:45 -0700230 }
Dave Borowitz01551272014-03-16 13:43:16 -0700231 return result;
Dave Borowitz9de65952012-08-13 16:09:45 -0700232 }
233
Dave Borowitz01551272014-03-16 13:43:16 -0700234 private static List<Map<String, String>> toSoyData(GitilesView view, List<Ref> refs,
235 String prefix) {
Dave Borowitz9de65952012-08-13 16:09:45 -0700236 List<Map<String, String>> result = Lists.newArrayListWithCapacity(refs.size());
237 for (Ref ref : refs) {
238 if (ref.getName().startsWith(prefix)) {
239 result.add(ImmutableMap.of(
240 "name", ref.getName().substring(prefix.length()),
241 "url", GitilesView.revision()
242 .copyFrom(view)
243 .setRevision(Revision.unpeeled(ref.getName(), ref.getObjectId()))
244 .toUrl()));
245 }
246 }
Dave Borowitz9de65952012-08-13 16:09:45 -0700247 return result;
248 }
Dave Borowitz9de65952012-08-13 16:09:45 -0700249}