001    /*
002     * SonarQube, open source software quality management tool.
003     * Copyright (C) 2008-2014 SonarSource
004     * mailto:contact AT sonarsource DOT com
005     *
006     * SonarQube 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     * SonarQube 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     */
020    package org.sonar.api.server.rule;
021    
022    import org.apache.commons.lang.StringEscapeUtils;
023    import org.apache.commons.lang.StringUtils;
024    import org.sonar.api.PropertyType;
025    
026    import java.util.List;
027    
028    import static com.google.common.collect.Lists.newArrayList;
029    
030    /**
031     * @since 4.2
032     */
033    public final class RuleParamType {
034    
035      private static final String OPTION_SEPARATOR = ",";
036    
037      public static final RuleParamType STRING = new RuleParamType("STRING");
038      public static final RuleParamType TEXT = new RuleParamType("TEXT");
039      public static final RuleParamType BOOLEAN = new RuleParamType("BOOLEAN");
040      public static final RuleParamType INTEGER = new RuleParamType("INTEGER");
041      public static final RuleParamType FLOAT = new RuleParamType("FLOAT");
042    
043      private static final String CSV_SPLIT_REGEX = ",(?=([^\"]*\"[^\"]*\")*[^\"]*$)";
044      private static final String VALUES_PARAM = "values";
045      private static final String MULTIPLE_PARAM = "multiple";
046      private static final String PARAMETER_SEPARATOR = "=";
047    
048      private final String type;
049      private final List<String> values;
050      private final boolean multiple;
051    
052      // format is "type|comma-separated list of options", for example "INTEGER" or "SINGLE_SELECT_LIST|foo=one,bar,baz=two"
053      private final String key;
054    
055      private RuleParamType(String type, String... options) {
056        this(type, false, options);
057      }
058    
059      private RuleParamType(String type, boolean multiple, String... values) {
060        this.type = type;
061        this.values = newArrayList(values);
062        StringBuilder sb = new StringBuilder();
063        sb.append(type);
064        if (multiple) {
065          sb.append(OPTION_SEPARATOR);
066          sb.append(MULTIPLE_PARAM + PARAMETER_SEPARATOR);
067          sb.append(Boolean.toString(multiple));
068        }
069        if (values.length > 0) {
070          sb.append(OPTION_SEPARATOR);
071          sb.append(VALUES_PARAM + PARAMETER_SEPARATOR);
072          sb.append(StringEscapeUtils.escapeCsv(valuesToCsv(values)));
073        }
074        this.key = sb.toString();
075        this.multiple = multiple;
076      }
077    
078      private String valuesToCsv(String... values) {
079        StringBuilder sb = new StringBuilder();
080        for (String value : values) {
081          sb.append(StringEscapeUtils.escapeCsv(value));
082          sb.append(OPTION_SEPARATOR);
083        }
084        return sb.toString();
085      }
086    
087      public String type() {
088        return type;
089      }
090    
091      public List<String> values() {
092        return values;
093      }
094    
095      public boolean multiple() {
096        return multiple;
097      }
098    
099      public static RuleParamType singleListOfValues(String... acceptedValues) {
100        // reuse the same type as plugin properties in order to
101        // benefit from shared helpers (validation, HTML component)
102        String type = PropertyType.SINGLE_SELECT_LIST.name();
103        return new RuleParamType(type, acceptedValues);
104      }
105    
106      public static RuleParamType multipleListOfValues(String... acceptedValues) {
107        // reuse the same type as plugin properties in order to
108        // benefit from shared helpers (validation, HTML component)
109        String type = PropertyType.SINGLE_SELECT_LIST.name();
110        return new RuleParamType(type, true, acceptedValues);
111      }
112    
113      // TODO validate format
114      public static RuleParamType parse(String s) {
115        // deprecated formats
116        if ("i".equals(s) || "i{}".equals(s)) {
117          return INTEGER;
118        }
119        if ("s".equals(s) || "s{}".equals(s) || "r".equals(s) || "REGULAR_EXPRESSION".equals(s)) {
120          return STRING;
121        }
122        if ("b".equals(s)) {
123          return BOOLEAN;
124        }
125        if (s.startsWith("s[")) {
126          String values = StringUtils.substringBetween(s, "[", "]");
127          return multipleListOfValues(StringUtils.split(values, ','));
128        }
129    
130        // standard format
131        String format = StringUtils.substringBefore(s, OPTION_SEPARATOR);
132        String values = null;
133        boolean multiple = false;
134        String[] options = s.split(CSV_SPLIT_REGEX);
135        for (String option : options) {
136          String opt = StringEscapeUtils.unescapeCsv(option);
137          if (opt.startsWith(VALUES_PARAM + PARAMETER_SEPARATOR)) {
138            values = StringEscapeUtils.unescapeCsv(StringUtils.substringAfter(opt, VALUES_PARAM + PARAMETER_SEPARATOR));
139          } else if (opt.startsWith(MULTIPLE_PARAM + PARAMETER_SEPARATOR)) {
140            multiple = Boolean.parseBoolean(StringUtils.substringAfter(opt, MULTIPLE_PARAM + PARAMETER_SEPARATOR));
141          }
142        }
143        if (values == null || StringUtils.isBlank(values)) {
144          return new RuleParamType(format);
145        }
146        return new RuleParamType(format, multiple, values.split(CSV_SPLIT_REGEX));
147      }
148    
149      @Override
150      public boolean equals(Object o) {
151        if (this == o) {
152          return true;
153        }
154        if (o == null || getClass() != o.getClass()) {
155          return false;
156        }
157    
158        RuleParamType that = (RuleParamType) o;
159        return key.equals(that.key);
160      }
161    
162      @Override
163      public int hashCode() {
164        return key.hashCode();
165      }
166    
167      @Override
168      public String toString() {
169        return key;
170      }
171    }