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.config;
021
022import java.util.Optional;
023import org.sonar.api.batch.ScannerSide;
024import org.sonar.api.ce.ComputeEngineSide;
025import org.sonar.api.config.internal.MapSettings;
026import org.sonar.api.server.ServerSide;
027import org.sonarsource.api.sonarlint.SonarLintSide;
028
029/**
030 * Component to get effective configuration. Values of properties depend on the runtime environment:
031 * <ul>
032 *   <li>immutable project or module configuration in scanner.</li>
033 *   <li>global configuration in web server. It does not allow to get the settings overridden on projects.</li>
034 *   <li>project configuration in Compute Engine.</li>
035 * </ul>
036 *
037 * <h3>Usage</h3>
038 * <pre>
039 * public class MyExtension {
040 *
041 *   private final Configuration config;
042 *
043 *   public MyExtension(Configuration config) {
044 *     this.config = config;
045 *   }
046 *   public void doSomething() {
047 *     String fooValue = config.get("sonar.foo").orElse(null);
048 *     // ..
049 *   }
050 * }
051 * </pre>
052 *
053 * <h3>Scanner example</h3>
054 * Scanner sensor can get the reference on Configuration directly through SensorContext,
055 * without injecting the component into constructor.
056 *
057 * <pre>
058 * public class MySensor implements Sensor {
059 *   {@literal @}Override
060 *   public void execute(SensorContext context) {
061 *     String fooValue = context.config().get("sonar.foo").orElse(null);
062 *     // ..
063 *   }
064 * }
065 * </pre>
066 *
067 * <p>
068 * For testing, and only for testing, the in-memory implementation {@link MapSettings} can be used.
069 * <pre>
070 * {@literal @}Test
071 * public void my_test() {
072 *   MapSettings settings = new MapSettings();
073 *   settings.setProperty("foo", "bar");
074 *   MyExtension underTest = new MyExtension(settings.asConfig());
075 *   // ...
076 * }
077 * </pre>
078 *
079 * @see MapSettings
080 * @see PropertyDefinition
081 * @since 6.5
082 */
083@ScannerSide
084@ServerSide
085@ComputeEngineSide
086@SonarLintSide
087public interface Configuration {
088
089  /**
090   * The effective value of the specified property. Can return {@code Optional#empty()} if the property is not set and has no defined default value.
091   * <p>
092   * If the property is encrypted with a secret key, then the returned value is decrypted.
093   * </p>
094   *
095   * @throws IllegalStateException if value is encrypted but fails to be decrypted.
096   */
097  Optional<String> get(String key);
098
099  /**
100   * @return {@code true} if the property has a non-default value, else {@code false}.
101   */
102  boolean hasKey(String key);
103
104  /**
105   * Used to read multi-valued properties. 
106   * <p>
107   * See {@link PropertyDefinition.Builder#multiValues(boolean)}
108   * Multi-valued properties coming from scanner are parsed as CSV lines (ie comma separator and optional double quotes to escape values).
109   * Non quoted values are trimmed.
110   * <br>
111   * Examples :
112   * <ul>
113   * <li>"one,two,three " -&gt; ["one", "two", "three"]</li>
114   * <li>"  one, two, three " -&gt; ["one", "two", "three"]</li>
115   * <li>"one, , three" -&gt; ["one", "", "three"]</li>
116   * <li>"one,\"two,three\",\" four \"" -&gt; ["one", "two,three", " four "]</li>
117   * </ul>
118   */
119  String[] getStringArray(String key);
120
121  /**
122   * Effective value as boolean. It is {@code empty} if {@link #get(String)} is empty or if it
123   * does not return {@code "true"}, even if it's not a boolean representation.
124   * @return {@code true} if the effective value is {@code "true"}, {@code false} for any other non empty value. 
125   * If the property does not have value nor default value, then {@code empty} is returned.
126   */
127  default Optional<Boolean> getBoolean(String key) {
128    return get(key).map(String::trim).map(Boolean::parseBoolean);
129  }
130
131  /**
132   * Effective value as {@code int}.
133   * @return the value as {@code int}. If the property does not have value nor default value, then {@code empty} is returned.
134   * @throws NumberFormatException if value is not empty and is not a parsable integer
135   */
136  default Optional<Integer> getInt(String key) {
137    try {
138      return get(key).map(String::trim).map(Integer::parseInt);
139    } catch (NumberFormatException e) {
140      throw new IllegalStateException(String.format("The property '%s' is not an int value: %s", key, e.getMessage()));
141    }
142  }
143
144  /**
145   * Effective value as {@code long}.
146   * @return the value as {@code long}. If the property does not have value nor default value, then {@code empty} is returned.
147   * @throws NumberFormatException if value is not empty and is not a parsable {@code long}
148   */
149  default Optional<Long> getLong(String key) {
150    try {
151      return get(key).map(String::trim).map(Long::parseLong);
152    } catch (NumberFormatException e) {
153      throw new IllegalStateException(String.format("The property '%s' is not an long value: %s", key, e.getMessage()));
154    }
155  }
156
157  /**
158   * Effective value as {@code Float}.
159   * @return the value as {@code Float}. If the property does not have value nor default value, then {@code empty} is returned.
160   * @throws NumberFormatException if value is not empty and is not a parsable number
161   */
162  default Optional<Float> getFloat(String key) {
163    try {
164      return get(key).map(String::trim).map(Float::valueOf);
165    } catch (NumberFormatException e) {
166      throw new IllegalStateException(String.format("The property '%s' is not an float value: %s", key, e.getMessage()));
167    }
168  }
169
170  /**
171   * Effective value as {@code Double}.
172   * @return the value as {@code Double}. If the property does not have value nor default value, then {@code empty} is returned.
173   * @throws NumberFormatException if value is not empty and is not a parsable number
174   */
175  default Optional<Double> getDouble(String key) {
176    try {
177      return get(key).map(String::trim).map(Double::valueOf);
178    } catch (NumberFormatException e) {
179      throw new IllegalStateException(String.format("The property '%s' is not an double value: %s", key, e.getMessage()));
180    }
181  }
182
183}