001/*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2008-2012 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * Sonar 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 * Sonar 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
017 * License along with Sonar; if not, write to the Free Software
018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
019 */
020package org.sonar.api.config;
021
022import com.google.common.collect.ImmutableList;
023import org.apache.commons.lang.ArrayUtils;
024import org.apache.commons.lang.StringUtils;
025import org.apache.commons.lang.math.NumberUtils;
026import org.sonar.api.Property;
027import org.sonar.api.PropertyType;
028
029import javax.annotation.Nullable;
030
031import java.util.List;
032
033/**
034 * @since 3.0
035 */
036public final class PropertyDefinition {
037
038  public static final class Result {
039    private static final Result SUCCESS = new Result(null);
040
041    private String errorKey = null;
042
043    private static Result newError(String key) {
044      return new Result(key);
045    }
046
047    @Nullable
048    private Result(@Nullable String errorKey) {
049      this.errorKey = errorKey;
050    }
051
052    public boolean isValid() {
053      return StringUtils.isBlank(errorKey);
054    }
055
056    @Nullable
057    public String getErrorKey() {
058      return errorKey;
059    }
060  }
061
062  private final String key;
063  private final String defaultValue;
064  private final String name;
065  private final PropertyType type;
066  private final String[] options;
067  private final String description;
068  private final String category;
069  private final boolean onProject;
070  private final boolean onModule;
071  private final boolean isGlobal;
072  private final boolean multiValues;
073  private final String propertySetKey;
074  private final List<PropertyFieldDefinition> fields;
075
076  private PropertyDefinition(Property annotation) {
077    this.key = annotation.key();
078    this.name = annotation.name();
079    this.defaultValue = annotation.defaultValue();
080    this.description = annotation.description();
081    this.isGlobal = annotation.global();
082    this.onProject = annotation.project();
083    this.onModule = annotation.module();
084    this.category = annotation.category();
085    this.type = fixType(annotation.key(), annotation.type());
086    this.options = annotation.options();
087    this.multiValues = annotation.multiValues();
088    this.propertySetKey = annotation.propertySetKey();
089    this.fields = ImmutableList.copyOf(PropertyFieldDefinition.create(annotation.fields()));
090  }
091
092  private PropertyDefinition(String key, PropertyType type, String[] options) {
093    this.key = key;
094    this.name = null;
095    this.defaultValue = null;
096    this.description = null;
097    this.isGlobal = true;
098    this.onProject = false;
099    this.onModule = false;
100    this.category = null;
101    this.type = type;
102    this.options = options;
103    this.multiValues = false;
104    this.propertySetKey = null;
105    this.fields = null;
106  }
107
108  private static PropertyType fixType(String key, PropertyType type) {
109    // Auto-detect passwords and licenses for old versions of plugins that
110    // do not declare property types
111    if (type == PropertyType.STRING) {
112      if (StringUtils.endsWith(key, ".password.secured")) {
113        return PropertyType.PASSWORD;
114      } else if (StringUtils.endsWith(key, ".license.secured")) {
115        return PropertyType.LICENSE;
116      }
117    }
118    return type;
119  }
120
121  public static PropertyDefinition create(Property annotation) {
122    return new PropertyDefinition(annotation);
123  }
124
125  public static PropertyDefinition create(String key, PropertyType type, String[] options) {
126    return new PropertyDefinition(key, type, options);
127  }
128
129  public Result validate(@Nullable String value) {
130    return validate(type, value, options);
131  }
132
133  static Result validate(PropertyType type, @Nullable String value, String[] options) {
134    if (StringUtils.isNotBlank(value)) {
135      if (type == PropertyType.BOOLEAN) {
136        if (!StringUtils.equalsIgnoreCase(value, "true") && !StringUtils.equalsIgnoreCase(value, "false")) {
137          return Result.newError("notBoolean");
138        }
139      } else if (type == PropertyType.INTEGER) {
140        if (!NumberUtils.isDigits(value)) {
141          return Result.newError("notInteger");
142        }
143      } else if (type == PropertyType.FLOAT) {
144        try {
145          Double.parseDouble(value);
146        } catch (NumberFormatException e) {
147          return Result.newError("notFloat");
148        }
149      } else if (type == PropertyType.SINGLE_SELECT_LIST) {
150        if (!ArrayUtils.contains(options, value)) {
151          return Result.newError("notInOptions");
152        }
153      }
154    }
155    return Result.SUCCESS;
156  }
157
158  public String getKey() {
159    return key;
160  }
161
162  public String getDefaultValue() {
163    return defaultValue;
164  }
165
166  public String getName() {
167    return name;
168  }
169
170  public PropertyType getType() {
171    return type;
172  }
173
174  public String[] getOptions() {
175    return options.clone();
176  }
177
178  public String getDescription() {
179    return description;
180  }
181
182  public String getCategory() {
183    return category;
184  }
185
186  public boolean isOnProject() {
187    return onProject;
188  }
189
190  public boolean isOnModule() {
191    return onModule;
192  }
193
194  public boolean isGlobal() {
195    return isGlobal;
196  }
197
198  /**
199   * @since 3.3
200   */
201  public boolean isMultiValues() {
202    return multiValues;
203  }
204
205  /**
206   * @since 3.3
207   */
208  public String getPropertySetKey() {
209    return propertySetKey;
210  }
211
212  /**
213   * @since 3.3
214   */
215  public List<PropertyFieldDefinition> getFields() {
216    return fields;
217  }
218}