com.google.gerrit.server.project.ListBranches.java Source code

Java tutorial

Introduction

Here is the source code for com.google.gerrit.server.project.ListBranches.java

Source

// Copyright (C) 2013 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.server.project;

import com.google.common.base.Predicate;
import com.google.common.base.Strings;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Sets;
import com.google.gerrit.extensions.api.projects.BranchInfo;
import com.google.gerrit.extensions.common.ActionInfo;
import com.google.gerrit.extensions.common.WebLinkInfo;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.extensions.restapi.RestView;
import com.google.gerrit.extensions.webui.UiAction;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.WebLinks;
import com.google.gerrit.server.extensions.webui.UiActions;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.inject.Inject;
import com.google.inject.util.Providers;

import dk.brics.automaton.RegExp;
import dk.brics.automaton.RunAutomaton;

import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.kohsuke.args4j.Option;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.TreeMap;

public class ListBranches implements RestReadView<ProjectResource> {
    private final GitRepositoryManager repoManager;
    private final DynamicMap<RestView<BranchResource>> branchViews;
    private final WebLinks webLinks;

    @Option(name = "--limit", aliases = { "-n" }, metaVar = "CNT", usage = "maximum number of branches to list")
    public void setLimit(int limit) {
        this.limit = limit;
    }

    @Option(name = "--start", aliases = { "-s" }, metaVar = "CNT", usage = "number of branches to skip")
    public void setStart(int start) {
        this.start = start;
    }

    @Option(name = "--match", aliases = { "-m" }, metaVar = "MATCH", usage = "match branches substring")
    public void setMatchSubstring(String matchSubstring) {
        this.matchSubstring = matchSubstring;
    }

    @Option(name = "--regex", aliases = { "-r" }, metaVar = "REGEX", usage = "match branches regex")
    public void setMatchRegex(String matchRegex) {
        this.matchRegex = matchRegex;
    }

    private int limit;
    private int start;
    private String matchSubstring;
    private String matchRegex;

    @Inject
    public ListBranches(GitRepositoryManager repoManager, DynamicMap<RestView<BranchResource>> branchViews,
            WebLinks webLinks) {
        this.repoManager = repoManager;
        this.branchViews = branchViews;
        this.webLinks = webLinks;
    }

    @Override
    public List<BranchInfo> apply(ProjectResource rsrc)
            throws ResourceNotFoundException, IOException, BadRequestException {
        FluentIterable<BranchInfo> branches = allBranches(rsrc);
        branches = filterBranches(branches);
        if (start > 0) {
            branches = branches.skip(start);
        }
        if (limit > 0) {
            branches = branches.limit(limit);
        }
        return branches.toList();
    }

    private FluentIterable<BranchInfo> allBranches(ProjectResource rsrc)
            throws IOException, ResourceNotFoundException {
        List<Ref> refs;
        try (Repository db = repoManager.openRepository(rsrc.getNameKey())) {
            Collection<Ref> heads = db.getRefDatabase().getRefs(Constants.R_HEADS).values();
            refs = new ArrayList<>(heads.size() + 3);
            refs.addAll(heads);
            refs.addAll(db.getRefDatabase()
                    .exactRef(Constants.HEAD, RefNames.REFS_CONFIG, RefNames.REFS_USERS_DEFAULT).values());
        } catch (RepositoryNotFoundException noGitRepository) {
            throw new ResourceNotFoundException();
        }

        Set<String> targets = Sets.newHashSetWithExpectedSize(1);
        for (Ref ref : refs) {
            if (ref.isSymbolic()) {
                targets.add(ref.getTarget().getName());
            }
        }

        List<BranchInfo> branches = new ArrayList<>(refs.size());
        for (Ref ref : refs) {
            if (ref.isSymbolic()) {
                // A symbolic reference to another branch, instead of
                // showing the resolved value, show the name it references.
                //
                String target = ref.getTarget().getName();
                RefControl targetRefControl = rsrc.getControl().controlForRef(target);
                if (!targetRefControl.isVisible()) {
                    continue;
                }
                if (target.startsWith(Constants.R_HEADS)) {
                    target = target.substring(Constants.R_HEADS.length());
                }

                BranchInfo b = new BranchInfo();
                b.ref = ref.getName();
                b.revision = target;
                branches.add(b);

                if (!Constants.HEAD.equals(ref.getName())) {
                    b.canDelete = targetRefControl.canDelete() ? true : null;
                }
                continue;
            }

            RefControl refControl = rsrc.getControl().controlForRef(ref.getName());
            if (refControl.isVisible()) {
                branches.add(createBranchInfo(ref, refControl, targets));
            }
        }
        Collections.sort(branches, new BranchComparator());
        return FluentIterable.from(branches);
    }

    private static class BranchComparator implements Comparator<BranchInfo> {
        @Override
        public int compare(BranchInfo a, BranchInfo b) {
            return ComparisonChain.start().compareTrueFirst(isHead(a), isHead(b))
                    .compareTrueFirst(isConfig(a), isConfig(b)).compare(a.ref, b.ref).result();
        }

        private static boolean isHead(BranchInfo i) {
            return Constants.HEAD.equals(i.ref);
        }

        private static boolean isConfig(BranchInfo i) {
            return RefNames.REFS_CONFIG.equals(i.ref);
        }
    }

    private FluentIterable<BranchInfo> filterBranches(FluentIterable<BranchInfo> branches)
            throws BadRequestException {
        if (!Strings.isNullOrEmpty(matchSubstring)) {
            branches = branches.filter(new SubstringPredicate(matchSubstring));
        } else if (!Strings.isNullOrEmpty(matchRegex)) {
            branches = branches.filter(new RegexPredicate(matchRegex));
        }
        return branches;
    }

    private static class SubstringPredicate implements Predicate<BranchInfo> {
        private final String substring;

        private SubstringPredicate(String substring) {
            this.substring = substring.toLowerCase(Locale.US);
        }

        @Override
        public boolean apply(BranchInfo in) {
            String ref = in.ref;
            if (ref.startsWith(Constants.R_HEADS)) {
                ref = ref.substring(Constants.R_HEADS.length());
            }
            ref = ref.toLowerCase(Locale.US);
            return ref.contains(substring);
        }
    }

    private static class RegexPredicate implements Predicate<BranchInfo> {
        private final RunAutomaton a;

        private RegexPredicate(String regex) throws BadRequestException {
            if (regex.startsWith("^")) {
                regex = regex.substring(1);
                if (regex.endsWith("$") && !regex.endsWith("\\$")) {
                    regex = regex.substring(0, regex.length() - 1);
                }
            }
            try {
                a = new RunAutomaton(new RegExp(regex).toAutomaton());
            } catch (IllegalArgumentException e) {
                throw new BadRequestException(e.getMessage());
            }
        }

        @Override
        public boolean apply(BranchInfo in) {
            if (!in.ref.startsWith(Constants.R_HEADS)) {
                return a.run(in.ref);
            } else {
                return a.run(in.ref.substring(Constants.R_HEADS.length()));
            }
        }
    }

    private BranchInfo createBranchInfo(Ref ref, RefControl refControl, Set<String> targets) {
        BranchInfo info = new BranchInfo();
        info.ref = ref.getName();
        info.revision = ref.getObjectId() != null ? ref.getObjectId().name() : null;
        info.canDelete = !targets.contains(ref.getName()) && refControl.canDelete() ? true : null;
        for (UiAction.Description d : UiActions.from(branchViews,
                new BranchResource(refControl.getProjectControl(), info),
                Providers.of(refControl.getCurrentUser()))) {
            if (info.actions == null) {
                info.actions = new TreeMap<>();
            }
            info.actions.put(d.getId(), new ActionInfo(d));
        }
        FluentIterable<WebLinkInfo> links = webLinks
                .getBranchLinks(refControl.getProjectControl().getProject().getName(), ref.getName());
        info.webLinks = links.isEmpty() ? null : links.toList();
        return info;
    }
}