com.google.gitiles.TreeSoyData.java Source code

Java tutorial

Introduction

Here is the source code for com.google.gitiles.TreeSoyData.java

Source

// 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.gitiles.RevisionParser.PATH_SPLITTER;
import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Charsets;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.io.Files;
import com.google.gitiles.PathServlet.FileType;

import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.TreeWalk;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

/** Soy data converter for git trees. */
public class TreeSoyData {
    /**
     * Number of characters to display for a symlink target. Targets longer than
     * this are abbreviated for display in a tree listing.
     */
    private static final int MAX_SYMLINK_TARGET_LENGTH = 72;

    /**
     * Maximum number of bytes to load from a blob that claims to be a symlink. If
     * the blob is larger than this byte limit it will be displayed as a binary
     * file instead of as a symlink.
     */
    static final int MAX_SYMLINK_SIZE = 16 << 10;

    static String resolveTargetUrl(GitilesView view, String target) {
        if (target.startsWith("/")) {
            return null;
        }

        // simplifyPath() normalizes "a/../../" to "a", so manually check whether
        // the path leads above the git root.
        int depth = new StringTokenizer(view.getTreePath(), "/").countTokens();
        for (String part : PATH_SPLITTER.split(target)) {
            if (part.equals("..")) {
                depth--;
                if (depth < 0) {
                    return null;
                }
            } else if (!part.isEmpty() && !part.equals(".")) {
                depth++;
            }
        }

        String path = Files.simplifyPath(view.getTreePath() + "/" + target);
        return GitilesView.path().copyFrom(view).setTreePath(!path.equals(".") ? path : "").toUrl();
    }

    @VisibleForTesting
    static String getTargetDisplayName(String target) {
        if (target.length() <= MAX_SYMLINK_TARGET_LENGTH) {
            return target;
        } else {
            int lastSlash = target.lastIndexOf('/');
            // TODO(dborowitz): Doesn't abbreviate a long last path component.
            return lastSlash >= 0 ? "..." + target.substring(lastSlash) : target;
        }
    }

    private final RevWalk rw;
    private final GitilesView view;

    public TreeSoyData(RevWalk rw, GitilesView view) {
        this.rw = rw;
        this.view = view;
    }

    public Map<String, Object> toSoyData(ObjectId treeId, TreeWalk tw) throws MissingObjectException, IOException {
        List<Object> entries = Lists.newArrayList();
        GitilesView.Builder urlBuilder = GitilesView.path().copyFrom(view);
        while (tw.next()) {
            FileType type = FileType.forEntry(tw);
            String name = tw.getNameString();

            switch (view.getType()) {
            case PATH:
                urlBuilder.setTreePath(view.getTreePath() + "/" + name);
                break;
            case REVISION:
                // Got here from a tag pointing at a tree.
                urlBuilder.setTreePath(name);
                break;
            default:
                throw new IllegalStateException(
                        String.format("Cannot render TreeSoyData from %s view", view.getType()));
            }

            String url = urlBuilder.toUrl();
            if (type == FileType.TREE) {
                name += "/";
                url += "/";
            }
            Map<String, String> entry = Maps.newHashMapWithExpectedSize(4);
            entry.put("type", type.toString());
            entry.put("name", name);
            entry.put("url", url);
            if (type == FileType.SYMLINK) {
                String target = new String(rw.getObjectReader().open(tw.getObjectId(0)).getCachedBytes(),
                        Charsets.UTF_8);
                // TODO(dborowitz): Merge Shawn's changes before copying these methods
                // in.
                entry.put("targetName", getTargetDisplayName(target));
                String targetUrl = resolveTargetUrl(view, target);
                if (targetUrl != null) {
                    entry.put("targetUrl", targetUrl);
                }
            }
            entries.add(entry);
        }

        Map<String, Object> data = Maps.newHashMapWithExpectedSize(3);
        data.put("sha", treeId.name());
        data.put("entries", entries);

        if (view.getType() == GitilesView.Type.PATH && view.getRevision().getPeeledType() == OBJ_COMMIT) {
            data.put("logUrl", GitilesView.log().copyFrom(view).toUrl());
        }

        return data;
    }

    public Map<String, Object> toSoyData(ObjectId treeId) throws MissingObjectException, IOException {
        TreeWalk tw = new TreeWalk(rw.getObjectReader());
        tw.addTree(treeId);
        tw.setRecursive(false);
        return toSoyData(treeId, tw);
    }
}