Java tutorial
/* * Copyright (c) 2017 Cisco and/or its affiliates. * * 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 io.fd.maintainer.plugin.util; import static io.fd.maintainer.plugin.parser.ComponentPath.MatchLevel.MAX; import static io.fd.maintainer.plugin.parser.ComponentPath.MatchLevel.NONE; import com.google.common.collect.LinkedListMultimap; import com.google.common.collect.Multimap; import com.google.common.collect.Multiset; import com.google.gerrit.server.patch.PatchListEntry; import io.fd.maintainer.plugin.parser.ComponentInfo; import io.fd.maintainer.plugin.parser.ComponentPath; import io.fd.maintainer.plugin.parser.ComponentPath.MatchLevel; import io.fd.maintainer.plugin.parser.Maintainer; import io.fd.maintainer.plugin.service.ComponentReviewInfo; import io.fd.maintainer.plugin.service.ComponentReviewInfo.ComponentReviewInfoBuilder; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import javax.annotation.Nonnull; import org.apache.commons.lang3.StringUtils; import org.parboiled.common.Tuple2; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public final class MaintainersIndex implements ClosestMatch { private static final Logger LOG = LoggerFactory.getLogger(MaintainersIndex.class); private Map<ComponentPath, Set<Maintainer>> pathToMaintainersIndex; private Map<String, String> pathToComponentIndex; private Multimap<String, String> maintainerNameToComponentIndex; private Map<String, Boolean> reviewComponentIndex; public MaintainersIndex(@Nonnull final List<ComponentInfo> maintainers) { pathToMaintainersIndex = maintainers.stream() .flatMap(maintainersInfo -> maintainersInfo.getPaths().stream() .map(componentPath -> new Tuple2<>(componentPath, maintainersInfo.getMaintainers()))) .collect(Collectors.toMap(tuple -> tuple.a, tuple -> tuple.b)); pathToComponentIndex = new HashMap<>(); maintainers.forEach(maintainersInfo -> maintainersInfo.getPaths().forEach( componentPath -> pathToComponentIndex.put(componentPath.getPath(), maintainersInfo.getTitle()))); maintainerNameToComponentIndex = LinkedListMultimap.create(); maintainers.forEach(maintainersInfo -> maintainersInfo.getMaintainers() .forEach(maintainer -> maintainerNameToComponentIndex.put(maintainer.getName(), maintainersInfo.getTitle()))); reviewComponentIndex = maintainers.stream().collect( Collectors.toMap(ComponentInfo::getTitle, component -> !component.getMaintainers().isEmpty())); } private static int getPathLength(final String path) { return StringUtils.countMatches(path, "/"); } /** * Tells whether component has maintainers configured */ public boolean isReviewComponent(@Nonnull final String component) { return reviewComponentIndex.get(component); } public Set<String> getComponentsForMaintainer(@Nonnull final String name) { return new HashSet<>(maintainerNameToComponentIndex.get(name)); } public String getComponentForPath(@Nonnull final ComponentPath path) { return pathToComponentIndex.get(path.getPath()); } public Tuple2<Set<ComponentPath>, Set<ComponentPath>> getComponentPathsForEntry( @Nonnull final PatchListEntry entry) { final LinkedListMultimap<MatchLevel, ComponentPath> byMatchIndexOld = LinkedListMultimap.create(); final LinkedListMultimap<MatchLevel, ComponentPath> byMatchIndexNew = LinkedListMultimap.create(); pathToMaintainersIndex .forEach((key, value) -> byMatchIndexOld.put(key.matchAgainst(entry.getOldName()), key)); pathToMaintainersIndex .forEach((key, value) -> byMatchIndexNew.put(key.matchAgainst(entry.getNewName()), key)); final MatchLevel maxMatchLevelOld = maxMatchLevel(byMatchIndexOld.keys()); final MatchLevel maxMatchLevelNew = maxMatchLevel(byMatchIndexNew.keys()); final int mostSpecificLengthOld = mostSpecificPathLengthFromComponent(maxMatchLevelOld, byMatchIndexOld); final int mostSpecificLengthNew = mostSpecificPathLengthFromComponent(maxMatchLevelOld, byMatchIndexOld); final Set<ComponentPath> oldComponents = NONE == maxMatchLevelOld ? Collections.emptySet() : new HashSet<>(byMatchIndexOld.get(maxMatchLevelOld).stream() .filter(componentPath -> getPathLength(componentPath.getPath()) == mostSpecificLengthOld) .collect(Collectors.toList())); final Set<ComponentPath> newComponents = NONE == maxMatchLevelNew ? Collections.emptySet() : new HashSet<>(byMatchIndexNew.get(maxMatchLevelNew).stream() .filter(componentPath -> getPathLength(componentPath.getPath()) == mostSpecificLengthNew) .collect(Collectors.toList())); return new Tuple2<>(oldComponents, newComponents); } public ComponentReviewInfo getReviewInfoForPath(final String path) { LOG.debug("Getting maintainers for path {}", path); final LinkedListMultimap<MatchLevel, Tuple2<ComponentPath, Maintainer>> byMatchIndex = LinkedListMultimap .create(); pathToMaintainersIndex.forEach((key, value) -> value .forEach((entry -> byMatchIndex.put(key.matchAgainst(path), new Tuple2<>(key, entry))))); final MatchLevel maximumMatchLevel = maxMatchLevel(byMatchIndex.keys()); LOG.debug("Maximum match level for path {} = {}", path, maximumMatchLevel); // out of all that have maximum match level, we need only those that are most basically longest // allows to get /foo/bar/* over * or /foo/* final int mostSpecificPathLength = mostSpecificPathLengthFromTuple(maximumMatchLevel, byMatchIndex); if (NONE == maximumMatchLevel) { return new ComponentReviewInfoBuilder().setAffectedFile(path).createComponentReviewInfo(); } else { return byMatchIndex.get(maximumMatchLevel).stream() .filter(tuple -> getPathLength(tuple.a.getPath()) == mostSpecificPathLength) .peek(maintainer -> LOG.debug("Maintainer found [component={},reviewer={}]", maintainer.a, maintainer.b)) .map(tuple -> new ComponentReviewInfoBuilder().setAffectedFile(path) .setComponentName(getComponentForPath(tuple.a)) .setComponentMaintainers(pathToMaintainersIndex.get(tuple.a)) .createComponentReviewInfo()) .findAny() .orElse(new ComponentReviewInfoBuilder().setAffectedFile(path).createComponentReviewInfo()); } } private MatchLevel maxMatchLevel(final Multiset<MatchLevel> keys) { return keys.stream().max(MAX).orElse(NONE); } private int mostSpecificPathLengthFromTuple(final MatchLevel maximumMatchLevel, final LinkedListMultimap<MatchLevel, Tuple2<ComponentPath, Maintainer>> byMatchIndex) { return byMatchIndex.get(maximumMatchLevel).stream().map(tuple -> tuple.a).map(ComponentPath::getPath) .map(MaintainersIndex::getPathLength).max(Comparator.comparingInt(integer -> integer)).orElse(0); } private int mostSpecificPathLengthFromComponent(final MatchLevel maximumMatchLevel, final LinkedListMultimap<MatchLevel, ComponentPath> byMatchIndex) { return byMatchIndex.get(maximumMatchLevel).stream().map(ComponentPath::getPath) .map(MaintainersIndex::getPathLength).max(Comparator.comparingInt(integer -> integer)).orElse(0); } }