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 org.apache.commons.lang.ArrayUtils;
023import org.apache.commons.lang.StringUtils;
024import org.apache.commons.lang.math.NumberUtils;
025import org.sonar.api.Property;
026import org.sonar.api.PropertyType;
027
028import javax.annotation.Nullable;
029
030/**
031 * @since 3.0
032 */
033public final class PropertyDefinition {
034
035  public static final class Result {
036    private static final Result SUCCESS = new Result(null);
037
038    private String errorKey = null;
039
040    private static Result newError(String key) {
041      return new Result(key);
042    }
043
044    @Nullable
045    private Result(@Nullable String errorKey) {
046      this.errorKey = errorKey;
047    }
048
049    public boolean isValid() {
050      return StringUtils.isBlank(errorKey);
051    }
052
053    @Nullable
054    public String getErrorKey() {
055      return errorKey;
056    }
057  }
058
059  private String key;
060  private String defaultValue;
061  private String name;
062  private PropertyType type = PropertyType.STRING;
063  private String[] options;
064  private String description;
065  private String category;
066  private boolean onProject = false;
067  private boolean onModule = false;
068  private boolean isGlobal = true;
069
070  private PropertyDefinition(Property annotation) {
071    this.key = annotation.key();
072    this.name = annotation.name();
073    this.defaultValue = annotation.defaultValue();
074    this.description = annotation.description();
075    this.isGlobal = annotation.global();
076    this.onProject = annotation.project();
077    this.onModule = annotation.module();
078    this.category = annotation.category();
079    this.type = fixType(annotation.key(), annotation.type());
080    this.options = annotation.options();
081  }
082
083  private PropertyType fixType(String key, PropertyType type) {
084    // Auto-detect passwords and licenses for old versions of plugins that
085    // do not declare property types
086    PropertyType fix = type;
087    if (type == PropertyType.STRING) {
088      if (StringUtils.endsWith(key, ".password.secured")) {
089        fix = PropertyType.PASSWORD;
090      } else if (StringUtils.endsWith(key, ".license.secured")) {
091        fix = PropertyType.LICENSE;
092      }
093    }
094    return fix;
095  }
096
097  private PropertyDefinition(String key, PropertyType type, String[] options) {
098    this.key = key;
099    this.type = type;
100    this.options = options;
101  }
102
103  public static PropertyDefinition create(Property annotation) {
104    return new PropertyDefinition(annotation);
105  }
106
107  public static PropertyDefinition create(String key, PropertyType type, String[] options) {
108    return new PropertyDefinition(key, type, options);
109  }
110
111  public Result validate(@Nullable String value) {
112    // TODO REFACTORING REQUIRED HERE
113    Result result = Result.SUCCESS;
114    if (StringUtils.isNotBlank(value)) {
115      if (type == PropertyType.BOOLEAN) {
116        if (!StringUtils.equalsIgnoreCase(value, "true") && !StringUtils.equalsIgnoreCase(value, "false")) {
117          result = Result.newError("notBoolean");
118        }
119      } else if (type == PropertyType.INTEGER) {
120        if (!NumberUtils.isDigits(value)) {
121          result = Result.newError("notInteger");
122        }
123      } else if (type == PropertyType.FLOAT) {
124        try {
125          Double.parseDouble(value);
126        } catch (NumberFormatException e) {
127          result = Result.newError("notFloat");
128        }
129      } else if (type == PropertyType.SINGLE_SELECT_LIST) {
130        if (!ArrayUtils.contains(options, value)) {
131          result = Result.newError("notInOptions");
132        }
133      }
134    }
135    return result;
136  }
137
138  public String getKey() {
139    return key;
140  }
141
142  public String getDefaultValue() {
143    return defaultValue;
144  }
145
146  public String getName() {
147    return name;
148  }
149
150  public PropertyType getType() {
151    return type;
152  }
153
154  public String[] getOptions() {
155    return options.clone();
156  }
157
158  public String getDescription() {
159    return description;
160  }
161
162  public String getCategory() {
163    return category;
164  }
165
166  public boolean isOnProject() {
167    return onProject;
168  }
169
170  public boolean isOnModule() {
171    return onModule;
172  }
173
174  public boolean isGlobal() {
175    return isGlobal;
176  }
177}