Line data Source code
1 : // Copyright (C) 2013 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.query.change; 16 : 17 : import com.google.auto.value.AutoValue; 18 : import com.google.common.base.Splitter; 19 : import com.google.gerrit.index.query.QueryParseException; 20 : import java.util.ArrayList; 21 : import java.util.HashMap; 22 : import java.util.List; 23 : import java.util.Map; 24 : import java.util.regex.Matcher; 25 : import java.util.regex.Pattern; 26 : 27 : /** 28 : * This class is used to extract comma separated values in a predicate. 29 : * 30 : * <p>If tags for the values are present (e.g. "branch=jb_2.3,vote=approved") then the args are 31 : * placed in a map that maps tag to value (e.g., "branch" to "jb_2.3"). If no tag is present (e.g. 32 : * "jb_2.3,approved") then the args are placed into a positional list. Args may be mixed so some may 33 : * appear in the map and others in the positional list (e.g. "vote=approved,jb_2.3). 34 : */ 35 : public class PredicateArgs { 36 5 : private static final Pattern SPLIT_PATTERN = Pattern.compile("(>|>=|=|<|<=)([^=].*)$"); 37 : 38 : public List<String> positional; 39 : public Map<String, ValOp> keyValue; 40 : 41 5 : enum Operator { 42 5 : EQUAL("="), 43 5 : GREATER_EQUAL(">="), 44 5 : GREATER(">"), 45 5 : LESS_EQUAL("<="), 46 5 : LESS("<"); 47 : 48 : final String op; 49 : 50 5 : Operator(String op) { 51 5 : this.op = op; 52 5 : } 53 : } 54 : 55 : @AutoValue 56 5 : public abstract static class ValOp { 57 : abstract String value(); 58 : 59 : abstract Operator operator(); 60 : 61 : static ValOp create(String value, Operator operator) { 62 5 : return new AutoValue_PredicateArgs_ValOp(value, operator); 63 : } 64 : } 65 : 66 : /** 67 : * Parses query arguments into {@link #keyValue} and/or {@link #positional}.. 68 : * 69 : * <p>Labels for these arguments should be kept in ChangeQueryBuilder as {@code ARG_ID_[argument 70 : * name]}. 71 : * 72 : * @param args arguments to be parsed 73 : */ 74 5 : PredicateArgs(String args) throws QueryParseException { 75 5 : positional = new ArrayList<>(); 76 5 : keyValue = new HashMap<>(); 77 : 78 5 : for (String arg : Splitter.on(',').split(args)) { 79 5 : Matcher m = SPLIT_PATTERN.matcher(arg); 80 : 81 5 : if (!m.find()) { 82 4 : positional.add(arg); 83 5 : } else if (m.groupCount() == 2) { 84 5 : String key = arg.substring(0, m.start()); 85 5 : String op = m.group(1); 86 5 : String val = m.group(2); 87 5 : if (!keyValue.containsKey(key)) { 88 5 : keyValue.put(key, ValOp.create(val, getOperator(op))); 89 : } else { 90 0 : throw new QueryParseException("Duplicate key " + key); 91 : } 92 5 : } else { 93 0 : throw new QueryParseException("Invalid arg " + arg); 94 : } 95 5 : } 96 5 : } 97 : 98 : private Operator getOperator(String operator) { 99 5 : switch (operator) { 100 : case "<": 101 4 : return Operator.LESS; 102 : case "<=": 103 4 : return Operator.LESS_EQUAL; 104 : case "=": 105 5 : return Operator.EQUAL; 106 : case ">=": 107 4 : return Operator.GREATER_EQUAL; 108 : case ">": 109 4 : return Operator.GREATER; 110 : default: 111 0 : throw new IllegalArgumentException("Invalid Operator " + operator); 112 : } 113 : } 114 : }