com.google.devtools.moe.client.repositories.AbstractRevisionHistory.java Source code

Java tutorial

Introduction

Here is the source code for com.google.devtools.moe.client.repositories.AbstractRevisionHistory.java

Source

/*
 * Copyright (c) 2012 Google, Inc.
 *
 * 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.devtools.moe.client.repositories;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import com.google.devtools.moe.client.MoeProblem;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;
import java.util.Set;

/**
 * A skeletal implementation of {@link RevisionHistory} with common logic.
 */
public abstract class AbstractRevisionHistory implements RevisionHistory {

    private static final int MAX_REVISIONS_TO_SEARCH = 400;

    @Override
    public <T> T findRevisions(Revision revision, RevisionMatcher<T> matcher, SearchType searchType) {

        List<Revision> startingRevisions = (revision == null) ? findHeadRevisions() : ImmutableList.of(revision);

        if (startingRevisions.size() > 1 && searchType == SearchType.LINEAR) {
            throw new MoeProblem(
                    "MOE found a repository (%s) with multiple heads while trying to search linear history.",
                    startingRevisions.get(0).repositoryName());
        }

        RevisionGraph.Builder nonMatchingBuilder = RevisionGraph.builder(startingRevisions);
        ImmutableList.Builder<Revision> matchingBuilder = ImmutableList.builder();

        Deque<Revision> workList = new ArrayDeque<>();
        workList.addAll(startingRevisions);

        // Keep a visited list to make sure we don't visit the same change twice.
        Set<Revision> visited = Sets.newLinkedHashSet();
        visited.addAll(startingRevisions);

        while (!workList.isEmpty()) {
            Revision current = workList.removeFirst();
            if (!matcher.matches(current)) {
                RevisionMetadata metadata = getMetadata(current);
                nonMatchingBuilder.addRevision(current, metadata);

                List<Revision> parentsToSearch = metadata.parents;
                if (parentsToSearch.size() > 0 && searchType == SearchType.LINEAR) {
                    parentsToSearch = parentsToSearch.subList(0, 1);
                }
                for (Revision parent : parentsToSearch) {
                    // Don't add a visited parent to the search queue.
                    if (visited.add(parent)) {
                        workList.addLast(parent);
                    }
                }

                if (visited.size() > MAX_REVISIONS_TO_SEARCH) {
                    throw new MoeProblem(
                            "Couldn't find a matching revision for matcher (%s) from %s within %d revisions.",
                            matcher, (revision == null) ? "head" : revision, MAX_REVISIONS_TO_SEARCH);
                }
            } else {
                // Don't search past matching revisions.
                matchingBuilder.add(current);
            }
        }

        return matcher.makeResult(nonMatchingBuilder.build(), matchingBuilder.build());
    }

    /**
     * Find all head revisions (e.g. one each for all branches).
     */
    protected abstract List<Revision> findHeadRevisions();
}