| // 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.Preconditions.checkNotNull; | |
| import static org.eclipse.jgit.lib.Constants.OBJ_BLOB; | |
| import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT; | |
| import static org.eclipse.jgit.lib.Constants.OBJ_TAG; | |
| import static org.eclipse.jgit.lib.Constants.OBJ_TREE; | |
| import com.google.common.collect.ImmutableMap; | |
| import com.google.common.collect.ImmutableSet; | |
| import com.google.common.collect.Lists; | |
| import com.google.common.io.BaseEncoding; | |
| import com.google.gitiles.CommitData.Field; | |
| import com.google.gitiles.CommitJsonData.Commit; | |
| import com.google.gitiles.DateFormatter.Format; | |
| import com.google.gitiles.GitilesRequestFailureException.FailureReason; | |
| import java.io.IOException; | |
| import java.io.OutputStream; | |
| import java.io.Writer; | |
| import java.util.List; | |
| import java.util.Map; | |
| import javax.servlet.http.HttpServletRequest; | |
| import javax.servlet.http.HttpServletResponse; | |
| import org.eclipse.jgit.errors.IncorrectObjectTypeException; | |
| import org.eclipse.jgit.errors.MissingObjectException; | |
| import org.eclipse.jgit.http.server.ServletUtils; | |
| import org.eclipse.jgit.lib.Config; | |
| import org.eclipse.jgit.lib.Constants; | |
| import org.eclipse.jgit.lib.ObjectId; | |
| import org.eclipse.jgit.lib.ObjectLoader; | |
| import org.eclipse.jgit.lib.ObjectReader; | |
| import org.eclipse.jgit.lib.Repository; | |
| import org.eclipse.jgit.revwalk.RevCommit; | |
| import org.eclipse.jgit.revwalk.RevObject; | |
| import org.eclipse.jgit.revwalk.RevTag; | |
| import org.eclipse.jgit.revwalk.RevTree; | |
| import org.eclipse.jgit.revwalk.RevWalk; | |
| /** Serves an HTML page with detailed information about a ref. */ | |
| public class RevisionServlet extends BaseServlet { | |
| private static final ImmutableSet<Field> COMMIT_SOY_FIELDS = | |
| Field.setOf(CommitSoyData.DEFAULT_FIELDS, Field.DIFF_TREE); | |
| private static final ImmutableSet<Field> COMMIT_JSON_FIELDS = | |
| Field.setOf(CommitJsonData.DEFAULT_FIELDS, Field.DIFF_TREE); | |
| private static final long serialVersionUID = 1L; | |
| private final Linkifier linkifier; | |
| public RevisionServlet( | |
| GitilesAccess.Factory accessFactory, Renderer renderer, Linkifier linkifier) { | |
| super(renderer, accessFactory); | |
| this.linkifier = checkNotNull(linkifier, "linkifier"); | |
| } | |
| @Override | |
| protected void doGetHtml(HttpServletRequest req, HttpServletResponse res) throws IOException { | |
| GitilesView view = ViewFilter.getView(req); | |
| Repository repo = ServletUtils.getRepository(req); | |
| GitilesAccess access = getAccess(req); | |
| Config cfg = getAccess(req).getConfig(); | |
| try (RevWalk walk = new RevWalk(repo)) { | |
| DateFormatter df = new DateFormatter(access, Format.DEFAULT); | |
| List<RevObject> objects = listObjects(walk, view.getRevision()); | |
| List<Map<String, ?>> soyObjects = Lists.newArrayListWithCapacity(objects.size()); | |
| boolean hasBlob = false; | |
| boolean hasReadme = false; | |
| // TODO(sop): Allow caching commits by SHA-1 when no S cookie is sent. | |
| for (RevObject obj : objects) { | |
| try { | |
| switch (obj.getType()) { | |
| case OBJ_COMMIT: | |
| soyObjects.add( | |
| ImmutableMap.of( | |
| "type", | |
| Constants.TYPE_COMMIT, | |
| "data", | |
| new CommitSoyData() | |
| .setLinkifier(linkifier) | |
| .setArchiveFormat(getArchiveFormat(access)) | |
| .toSoyData(req, walk, (RevCommit) obj, COMMIT_SOY_FIELDS, df))); | |
| break; | |
| case OBJ_TREE: | |
| Map<String, Object> tree = | |
| new TreeSoyData( | |
| walk.getObjectReader(), view, cfg, (RevTree) obj, req.getRequestURI()) | |
| .toSoyData(obj); | |
| soyObjects.add(ImmutableMap.of("type", Constants.TYPE_TREE, "data", tree)); | |
| hasReadme = tree.containsKey("readmeHtml"); | |
| break; | |
| case OBJ_BLOB: | |
| soyObjects.add( | |
| ImmutableMap.of( | |
| "type", | |
| Constants.TYPE_BLOB, | |
| "data", | |
| new BlobSoyData(walk.getObjectReader(), view).toSoyData(obj))); | |
| hasBlob = true; | |
| break; | |
| case OBJ_TAG: | |
| soyObjects.add( | |
| ImmutableMap.of( | |
| "type", | |
| Constants.TYPE_TAG, | |
| "data", | |
| new TagSoyData(linkifier, req).toSoyData(walk, (RevTag) obj, df))); | |
| break; | |
| default: | |
| throw new GitilesRequestFailureException(FailureReason.UNSUPPORTED_OBJECT_TYPE); | |
| } | |
| } catch (MissingObjectException e) { | |
| throw new GitilesRequestFailureException(FailureReason.OBJECT_NOT_FOUND, e); | |
| } catch (IncorrectObjectTypeException e) { | |
| throw new GitilesRequestFailureException(FailureReason.INCORRECT_OBJECT_TYPE, e); | |
| } | |
| } | |
| renderHtml( | |
| req, | |
| res, | |
| "com.google.gitiles.templates.RevisionDetail.revisionDetail", | |
| ImmutableMap.of( | |
| "title", view.getRevision().getName(), | |
| "objects", soyObjects, | |
| "hasBlob", hasBlob, | |
| "hasReadme", hasReadme)); | |
| } | |
| } | |
| @Override | |
| protected void doGetText(HttpServletRequest req, HttpServletResponse res) throws IOException { | |
| GitilesView view = ViewFilter.getView(req); | |
| Repository repo = ServletUtils.getRepository(req); | |
| try (ObjectReader reader = repo.newObjectReader()) { | |
| ObjectLoader loader = reader.open(view.getRevision().getId()); | |
| if (loader.getType() != OBJ_COMMIT) { | |
| throw new GitilesRequestFailureException(FailureReason.UNSUPPORTED_OBJECT_TYPE); | |
| } | |
| PathServlet.setTypeHeader(res, loader.getType()); | |
| try (Writer writer = startRenderText(req, res); | |
| OutputStream out = BaseEncoding.base64().encodingStream(writer)) { | |
| loader.copyTo(out); | |
| } | |
| } | |
| } | |
| @Override | |
| protected void doGetJson(HttpServletRequest req, HttpServletResponse res) throws IOException { | |
| GitilesView view = ViewFilter.getView(req); | |
| Repository repo = ServletUtils.getRepository(req); | |
| try (RevWalk walk = new RevWalk(repo)) { | |
| DateFormatter df = new DateFormatter(getAccess(req), Format.DEFAULT); | |
| RevObject obj = walk.parseAny(view.getRevision().getId()); | |
| if (obj.getType() == OBJ_TAG) { | |
| obj = ((RevTag) obj).getObject(); | |
| } | |
| switch (obj.getType()) { | |
| case OBJ_COMMIT: | |
| renderJson( | |
| req, | |
| res, | |
| new CommitJsonData().toJsonData(req, walk, (RevCommit) obj, COMMIT_JSON_FIELDS, df), | |
| Commit.class); | |
| break; | |
| default: | |
| // TODO(dborowitz): Support showing other types. | |
| throw new GitilesRequestFailureException(FailureReason.UNSUPPORTED_OBJECT_TYPE); | |
| } | |
| } | |
| } | |
| // TODO(dborowitz): Extract this. | |
| static List<RevObject> listObjects(RevWalk walk, Revision rev) | |
| throws MissingObjectException, IOException { | |
| List<RevObject> objects = Lists.newArrayListWithExpectedSize(1); | |
| ObjectId id = rev.getId(); | |
| RevObject cur; | |
| while (true) { | |
| cur = walk.parseAny(id); | |
| objects.add(cur); | |
| if (cur.getType() != Constants.OBJ_TAG) { | |
| break; | |
| } | |
| id = ((RevTag) cur).getObject(); | |
| } | |
| if (cur.getType() == Constants.OBJ_COMMIT) { | |
| objects.add(walk.parseTree(((RevCommit) cur).getTree())); | |
| } | |
| return objects; | |
| } | |
| } |