001/*
002 * SonarQube
003 * Copyright (C) 2009-2017 SonarSource SA
004 * mailto:info AT sonarsource DOT com
005 *
006 * This program is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * This program is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public License
017 * along with this program; if not, write to the Free Software Foundation,
018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
019 */
020package org.sonar.api.server.rule;
021
022import java.util.List;
023import org.apache.commons.lang.StringEscapeUtils;
024import org.apache.commons.lang.StringUtils;
025import org.sonar.api.PropertyType;
026
027import static com.google.common.collect.Lists.newArrayList;
028
029/**
030 * @since 4.2
031 */
032public final class RuleParamType {
033
034  private static final String OPTION_SEPARATOR = ",";
035
036  public static final RuleParamType STRING = new RuleParamType("STRING");
037  public static final RuleParamType TEXT = new RuleParamType("TEXT");
038  public static final RuleParamType BOOLEAN = new RuleParamType("BOOLEAN");
039  public static final RuleParamType INTEGER = new RuleParamType("INTEGER");
040  public static final RuleParamType FLOAT = new RuleParamType("FLOAT");
041
042  private static final String CSV_SPLIT_REGEX = ",(?=([^\"]*\"[^\"]*\")*[^\"]*$)";
043  private static final String VALUES_PARAM = "values";
044  private static final String MULTIPLE_PARAM = "multiple";
045  private static final String PARAMETER_SEPARATOR = "=";
046
047  private final String type;
048  private final List<String> values;
049  private final boolean multiple;
050
051  // format is "type|comma-separated list of options", for example "INTEGER" or "SINGLE_SELECT_LIST|foo=one,bar,baz=two"
052  private final String key;
053
054  private RuleParamType(String type, String... options) {
055    this(type, false, options);
056  }
057
058  private RuleParamType(String type, boolean multiple, String... values) {
059    this.type = type;
060    this.values = newArrayList(values);
061    StringBuilder sb = new StringBuilder();
062    sb.append(type);
063    if (multiple) {
064      sb.append(OPTION_SEPARATOR);
065      sb.append(MULTIPLE_PARAM + PARAMETER_SEPARATOR);
066      sb.append(Boolean.toString(multiple));
067    }
068    if (values.length > 0) {
069      sb.append(OPTION_SEPARATOR);
070      sb.append(VALUES_PARAM + PARAMETER_SEPARATOR);
071      sb.append(StringEscapeUtils.escapeCsv(valuesToCsv(values)));
072    }
073    this.key = sb.toString();
074    this.multiple = multiple;
075  }
076
077  private static String valuesToCsv(String... values) {
078    StringBuilder sb = new StringBuilder();
079    for (String value : values) {
080      sb.append(StringEscapeUtils.escapeCsv(value));
081      sb.append(OPTION_SEPARATOR);
082    }
083    return sb.toString();
084  }
085
086  public String type() {
087    return type;
088  }
089
090  public List<String> values() {
091    return values;
092  }
093
094  public boolean multiple() {
095    return multiple;
096  }
097
098  public static RuleParamType singleListOfValues(String... acceptedValues) {
099    // reuse the same type as plugin properties in order to
100    // benefit from shared helpers (validation, HTML component)
101    String type = PropertyType.SINGLE_SELECT_LIST.name();
102    return new RuleParamType(type, acceptedValues);
103  }
104
105  public static RuleParamType multipleListOfValues(String... acceptedValues) {
106    // reuse the same type as plugin properties in order to
107    // benefit from shared helpers (validation, HTML component)
108    String type = PropertyType.SINGLE_SELECT_LIST.name();
109    return new RuleParamType(type, true, acceptedValues);
110  }
111
112  // TODO validate format
113  public static RuleParamType parse(String s) {
114    // deprecated formats
115    if ("i".equals(s) || "i{}".equals(s)) {
116      return INTEGER;
117    }
118    if ("s".equals(s) || "s{}".equals(s) || "r".equals(s) || "REGULAR_EXPRESSION".equals(s)) {
119      return STRING;
120    }
121    if ("b".equals(s)) {
122      return BOOLEAN;
123    }
124    if (s.startsWith("s[")) {
125      String values = StringUtils.substringBetween(s, "[", "]");
126      return multipleListOfValues(StringUtils.split(values, ','));
127    }
128
129    // standard format
130    String format = StringUtils.substringBefore(s, OPTION_SEPARATOR);
131    String values = null;
132    boolean multiple = false;
133    String[] options = s.split(CSV_SPLIT_REGEX);
134    for (String option : options) {
135      String opt = StringEscapeUtils.unescapeCsv(option);
136      if (opt.startsWith(VALUES_PARAM + PARAMETER_SEPARATOR)) {
137        values = StringEscapeUtils.unescapeCsv(StringUtils.substringAfter(opt, VALUES_PARAM + PARAMETER_SEPARATOR));
138      } else if (opt.startsWith(MULTIPLE_PARAM + PARAMETER_SEPARATOR)) {
139        multiple = Boolean.parseBoolean(StringUtils.substringAfter(opt, MULTIPLE_PARAM + PARAMETER_SEPARATOR));
140      }
141    }
142    if (values == null || StringUtils.isBlank(values)) {
143      return new RuleParamType(format);
144    }
145    return new RuleParamType(format, multiple, values.split(CSV_SPLIT_REGEX));
146  }
147
148  @Override
149  public boolean equals(Object o) {
150    if (this == o) {
151      return true;
152    }
153    if (o == null || getClass() != o.getClass()) {
154      return false;
155    }
156
157    RuleParamType that = (RuleParamType) o;
158    return key.equals(that.key);
159  }
160
161  @Override
162  public int hashCode() {
163    return key.hashCode();
164  }
165
166  @Override
167  public String toString() {
168    return key;
169  }
170}