| // Copyright 2012 Google Inc. All Rights Reserved. | |
| // | |
| // 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.gitiles; | |
| import static com.google.common.base.MoreObjects.firstNonNull; | |
| import static com.google.common.base.Preconditions.checkNotNull; | |
| import com.google.common.collect.ImmutableMap; | |
| import com.google.common.collect.ImmutableSet; | |
| import com.google.common.collect.Maps; | |
| import com.google.common.collect.Sets; | |
| import com.google.gitiles.CommitData.Field; | |
| import com.google.template.soy.jbcsrc.api.SoySauce; | |
| import java.io.IOException; | |
| import java.io.Writer; | |
| import java.util.Map; | |
| import java.util.Set; | |
| import javax.annotation.Nullable; | |
| import javax.servlet.http.HttpServletRequest; | |
| import org.eclipse.jgit.diff.DiffEntry; | |
| import org.eclipse.jgit.diff.DiffEntry.ChangeType; | |
| import org.eclipse.jgit.lib.Config; | |
| import org.eclipse.jgit.lib.ObjectId; | |
| import org.eclipse.jgit.revwalk.RevCommit; | |
| import org.eclipse.jgit.revwalk.RevWalk; | |
| public class LogSoyData { | |
| private static final ImmutableSet<Field> FIELDS = | |
| Sets.immutableEnumSet( | |
| Field.ABBREV_SHA, | |
| Field.SHA, | |
| Field.URL, | |
| Field.SHORT_MESSAGE, | |
| Field.MESSAGE, | |
| Field.AUTHOR, | |
| Field.COMMITTER, | |
| Field.BRANCHES, | |
| Field.TAGS); | |
| private static final ImmutableSet<Field> VERBOSE_FIELDS = Field.setOf(FIELDS, Field.DIFF_TREE); | |
| /** Behavior for the footer link when rendering streaming log data. */ | |
| public enum FooterBehavior { | |
| /** "Next" link that skips commits in the current view. */ | |
| NEXT, | |
| /** "More" link that starts from HEAD. */ | |
| LOG_HEAD; | |
| } | |
| private final HttpServletRequest req; | |
| private final GitilesView view; | |
| private final Set<Field> fields; | |
| private final String variant; | |
| private CommitSoyData csd; | |
| public LogSoyData(HttpServletRequest req, GitilesAccess access, String pretty) | |
| throws IOException { | |
| this.req = checkNotNull(req); | |
| this.view = checkNotNull(ViewFilter.getView(req)); | |
| checkNotNull(pretty); | |
| Config config = access.getConfig(); | |
| fields = | |
| config.getBoolean("logFormat", pretty, "verbose", false) || pretty.equals("fuller") | |
| ? VERBOSE_FIELDS | |
| : FIELDS; | |
| variant = firstNonNull(config.getString("logFormat", pretty, "variant"), pretty); | |
| } | |
| private void renderHtml(SoySauce.Renderer renderer, Appendable out) throws IOException { | |
| if (!renderer.renderHtml(out).result().isDone()) { | |
| throw new IOException("failed to render HTML"); | |
| } | |
| } | |
| public void renderStreaming( | |
| Paginator paginator, | |
| @Nullable String revision, | |
| Renderer renderer, | |
| Writer writer, | |
| DateFormatter df, | |
| FooterBehavior footerBehavior) | |
| throws IOException { | |
| renderHtml( | |
| renderer | |
| .newRenderer("com.google.gitiles.templates.LogDetail.logEntriesHeader") | |
| .setData(toHeaderSoyData(paginator, revision)), | |
| writer); | |
| SoySauce.Renderer entryRenderer = | |
| renderer.newRenderer("com.google.gitiles.templates.LogDetail.logEntryWrapper"); | |
| boolean renderedEntries = false; | |
| for (RevCommit c : paginator) { | |
| RevWalk walk = paginator.getWalk(); | |
| if (!walk.isRetainBody()) { | |
| walk.parseBody(c); | |
| } | |
| renderHtml(entryRenderer.setData(toEntrySoyData(paginator, c, df)), writer); | |
| renderedEntries = true; | |
| } | |
| if (!renderedEntries) { | |
| renderHtml(renderer.newRenderer("com.google.gitiles.templates.LogDetail.emptyLog"), writer); | |
| } | |
| renderHtml( | |
| renderer | |
| .newRenderer("com.google.gitiles.templates.LogDetail.logEntriesFooter") | |
| .setData(toFooterSoyData(paginator, revision, footerBehavior)), | |
| writer); | |
| } | |
| private Map<String, Object> toHeaderSoyData(Paginator paginator, @Nullable String revision) { | |
| Map<String, Object> data = Maps.newHashMapWithExpectedSize(1); | |
| ObjectId prev = paginator.getPreviousStart(); | |
| if (prev != null) { | |
| GitilesView.Builder prevView = copyAndCanonicalizeView(revision); | |
| if (prevView.getRevision().getId().equals(prev)) { | |
| prevView.removeParam(LogServlet.START_PARAM); | |
| } else { | |
| prevView.replaceParam(LogServlet.START_PARAM, prev.name()); | |
| } | |
| data.put("previousUrl", prevView.toUrl()); | |
| } | |
| return data; | |
| } | |
| private Map<String, Object> toEntrySoyData(Paginator paginator, RevCommit c, DateFormatter df) | |
| throws IOException { | |
| if (csd == null) { | |
| csd = new CommitSoyData(); | |
| } | |
| Map<String, Object> entry = csd.toSoyData(req, paginator.getWalk(), c, fields, df); | |
| DiffEntry rename = paginator.getRename(c); | |
| if (rename != null) { | |
| entry.put("rename", toRenameSoyData(rename)); | |
| } | |
| return ImmutableMap.of( | |
| "variant", variant, | |
| "entry", entry); | |
| } | |
| private @Nullable Map<String, Object> toRenameSoyData(DiffEntry entry) { | |
| if (entry == null) { | |
| return null; | |
| } | |
| ChangeType type = entry.getChangeType(); | |
| if (type != ChangeType.RENAME && type != ChangeType.COPY) { | |
| return null; | |
| } | |
| return ImmutableMap.<String, Object>of( | |
| "changeType", type.toString(), | |
| "oldPath", entry.getOldPath(), | |
| "newPath", entry.getNewPath(), | |
| "score", entry.getScore()); | |
| } | |
| private Map<String, Object> toFooterSoyData( | |
| Paginator paginator, @Nullable String revision, FooterBehavior behavior) { | |
| switch (behavior) { | |
| case NEXT: | |
| ObjectId next = paginator.getNextStart(); | |
| if (next == null) { | |
| return ImmutableMap.of(); | |
| } | |
| return ImmutableMap.of( | |
| "nextUrl", | |
| copyAndCanonicalizeView(revision) | |
| .replaceParam(LogServlet.START_PARAM, next.name()) | |
| .toUrl(), | |
| "nextText", | |
| "Next"); | |
| case LOG_HEAD: | |
| return ImmutableMap.of( | |
| "nextUrl", GitilesView.log().copyFrom(view).toUrl(), "nextText", "More"); | |
| default: | |
| throw new IllegalStateException("unknown footer behavior: " + behavior); | |
| } | |
| } | |
| private GitilesView.Builder copyAndCanonicalizeView(String revision) { | |
| // Canonicalize the view by using full SHAs. | |
| GitilesView.Builder copy = GitilesView.log().copyFrom(view); | |
| if (!Revision.isNull(view.getRevision())) { | |
| copy.setRevision(view.getRevision()); | |
| } else if (revision != null) { | |
| copy.setRevision(Revision.named(revision)); | |
| } else { | |
| copy.setRevision(Revision.NULL); | |
| } | |
| if (!Revision.isNull(view.getOldRevision())) { | |
| copy.setOldRevision(view.getOldRevision()); | |
| } | |
| return copy; | |
| } | |
| } |