Line data Source code
1 : // Copyright (C) 2015 The Android Open Source Project 2 : // 3 : // Licensed under the Apache License, Version 2.0 (the "License"); 4 : // you may not use this file except in compliance with the License. 5 : // You may obtain a copy of the License at 6 : // 7 : // http://www.apache.org/licenses/LICENSE-2.0 8 : // 9 : // Unless required by applicable law or agreed to in writing, software 10 : // distributed under the License is distributed on an "AS IS" BASIS, 11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 : // See the License for the specific language governing permissions and 13 : // limitations under the License. 14 : 15 : package com.google.gerrit.server.project; 16 : 17 : import static com.google.common.collect.ImmutableList.toImmutableList; 18 : 19 : import com.google.common.base.Strings; 20 : import com.google.common.collect.ImmutableList; 21 : import com.google.gerrit.extensions.api.projects.RefInfo; 22 : import com.google.gerrit.extensions.restapi.BadRequestException; 23 : import dk.brics.automaton.RegExp; 24 : import dk.brics.automaton.RunAutomaton; 25 : import java.util.List; 26 : import java.util.Locale; 27 : import java.util.stream.Stream; 28 : 29 : public class RefFilter<T extends RefInfo> { 30 : private final String prefix; 31 : private String matchSubstring; 32 : private String matchRegex; 33 : private int start; 34 : private int limit; 35 : 36 10 : public RefFilter(String prefix) { 37 10 : this.prefix = prefix; 38 10 : } 39 : 40 : public RefFilter<T> subString(String subString) { 41 10 : this.matchSubstring = subString; 42 10 : return this; 43 : } 44 : 45 : public RefFilter<T> regex(String regex) { 46 10 : this.matchRegex = regex; 47 10 : return this; 48 : } 49 : 50 : public RefFilter<T> start(int start) { 51 10 : this.start = start; 52 10 : return this; 53 : } 54 : 55 : public RefFilter<T> limit(int limit) { 56 10 : this.limit = limit; 57 10 : return this; 58 : } 59 : 60 : public ImmutableList<T> filter(List<T> refs) throws BadRequestException { 61 10 : if (!Strings.isNullOrEmpty(matchSubstring) && !Strings.isNullOrEmpty(matchRegex)) { 62 2 : throw new BadRequestException("specify exactly one of m/r"); 63 : } 64 10 : Stream<T> results = refs.stream(); 65 10 : if (!Strings.isNullOrEmpty(matchSubstring)) { 66 2 : String lowercaseSubstring = matchSubstring.toLowerCase(Locale.US); 67 2 : results = results.filter(refInfo -> matchesSubstring(prefix, lowercaseSubstring, refInfo)); 68 10 : } else if (!Strings.isNullOrEmpty(matchRegex)) { 69 2 : RunAutomaton a = parseRegex(matchRegex); 70 2 : results = results.filter(refInfo -> matchesRegex(prefix, a, refInfo)); 71 : } 72 10 : if (start > 0) { 73 2 : results = results.skip(start); 74 : } 75 10 : if (limit > 0) { 76 2 : results = results.limit(limit); 77 : } 78 10 : return results.collect(toImmutableList()); 79 : } 80 : 81 : private static <T extends RefInfo> boolean matchesSubstring( 82 : String prefix, String lowercaseSubstring, T refInfo) { 83 2 : String ref = refInfo.ref; 84 2 : if (ref.startsWith(prefix)) { 85 2 : ref = ref.substring(prefix.length()); 86 : } 87 2 : ref = ref.toLowerCase(Locale.US); 88 2 : return ref.contains(lowercaseSubstring); 89 : } 90 : 91 : private static RunAutomaton parseRegex(String regex) throws BadRequestException { 92 2 : if (regex.startsWith("^")) { 93 1 : regex = regex.substring(1); 94 1 : if (regex.endsWith("$") && !regex.endsWith("\\$")) { 95 1 : regex = regex.substring(0, regex.length() - 1); 96 : } 97 : } 98 : try { 99 2 : return new RunAutomaton(new RegExp(regex).toAutomaton()); 100 0 : } catch (IllegalArgumentException e) { 101 0 : throw new BadRequestException(e.getMessage()); 102 : } 103 : } 104 : 105 : private static <T extends RefInfo> boolean matchesRegex( 106 : String prefix, RunAutomaton a, T refInfo) { 107 2 : String ref = refInfo.ref; 108 2 : if (ref.startsWith(prefix)) { 109 2 : ref = ref.substring(prefix.length()); 110 : } 111 2 : return a.run(ref); 112 : } 113 : }