import java.util.ArrayList;
import java.util.Date;
import java.util.List;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.utils.DateUtils;
import org.sonar.api.utils.SonarException;
import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
import static org.sonar.api.utils.DateUtils.parseDateQuietly;
import static org.sonar.api.utils.DateUtils.parseDateTimeQuietly;
private static long parseLong(String key, String value) {
try {
return Long.parseLong(value);
- } catch (NumberFormatException expection) {
+ } catch (NumberFormatException exception) {
throw new IllegalArgumentException(String.format("The '%s' parameter cannot be parsed as a long value: %s", key, value));
}
}
+ @Beta
+ public <T> Param<T> getParam(String key, BiFunction<Request, String, T> retrieveAndValidate) {
+ if (hasParam(key)) {
+ return GenericParam.present(retrieveAndValidate.apply(this, key));
+ }
+ return AbsentParam.absent();
+ }
+
+ @Beta
+ public StringParam getParam(String key, Consumer<String> validate) {
+ if (hasParam(key)) {
+ String value = this.param(key);
+ validate.accept(value);
+ return StringParamImpl.present(value);
+ }
+ return AbsentStringParam.absent();
+ }
+
+ @Beta
+ public StringParam getParam(String key) {
+ if (hasParam(key)) {
+ return StringParamImpl.present(this.param(key));
+ }
+ return AbsentStringParam.absent();
+ }
+
/**
* Allows a web service to call another web service.
* @see LocalConnector
String getFileName();
}
+
+ /**
+ * Represents a Request parameter, provides information whether is was specified or not (check {@link #isPresent()})
+ * and utility method to nicely handles cases where the parameter is not present.
+ */
+ @Beta
+ public interface Param<T> {
+ boolean isPresent();
+
+ /**
+ * @return the value of the parameter
+ *
+ * @throws IllegalStateException if param is not present.
+ */
+ @CheckForNull
+ T getValue();
+
+ @CheckForNull
+ T or(Supplier<T> defaultValueSupplier);
+ }
+
+ /**
+ * Implementation of {@link Param} where the param is not present.
+ */
+ private enum AbsentParam implements Param<Object> {
+ INSTANCE;
+
+ @SuppressWarnings("unchecked")
+ protected static <T> Param<T> absent() {
+ return (Param<T>) INSTANCE;
+ }
+
+ /**
+ * Always returns true.
+ */
+ @Override
+ public boolean isPresent() {
+ return false;
+ }
+
+ /**
+ * Always throws a {@link IllegalStateException}.
+ */
+ @Override
+ public Object getValue() {
+ throw createGetValueISE();
+ }
+
+ /**
+ * Always returns the value supplied by {@code defaultValueSupplier}.
+ */
+ @Override
+ @CheckForNull
+ public Object or(Supplier<Object> defaultValueSupplier) {
+ return checkDefaultValueSupplier(defaultValueSupplier).get();
+ }
+ }
+
+ /**
+ * Implementation of {@link Param} where the param is present.
+ */
+ private static final class GenericParam<T> implements Param<T> {
+ private final T value;
+
+ private GenericParam(T value) {
+ this.value = value;
+ }
+
+ static <T> Param<T> present(T value) {
+ return new GenericParam<>(value);
+ }
+
+ /**
+ * Always returns true.
+ */
+ @Override
+ public boolean isPresent() {
+ return true;
+ }
+
+ /**
+ * @return the value of the parameter
+ *
+ * @throws IllegalStateException if param is not present.
+ */
+ @Override
+ @CheckForNull
+ public T getValue() {
+ return value;
+ }
+
+ /**
+ * Always returns value of the parameter.
+ *
+ * @throws NullPointerException As per the inherited contract, {@code defaultValueSupplier} can't be null
+ */
+ @Override
+ @CheckForNull
+ public T or(Supplier<T> defaultValueSupplier) {
+ checkDefaultValueSupplier(defaultValueSupplier);
+ return value;
+ }
+ }
+
+ /**
+ * Extends {@link Param} with convenience methods specific to the type {@link String}.
+ */
+ interface StringParam extends Param<String> {
+ /**
+ * Returns a {@link StringParam} object which methods {@link #getValue()} and {@link #or(Supplier)} will
+ * return {@code null} rather than an empty String when the param is present and its value is an empty String.
+ */
+ StringParam emptyAsNull();
+ }
+
+ /**
+ * Implementation of {@link StringParam} where the param is not present.
+ */
+ private enum AbsentStringParam implements StringParam {
+ INSTANCE;
+
+ protected static StringParam absent() {
+ return INSTANCE;
+ }
+
+ /**
+ * Always returns false.
+ */
+ @Override
+ public boolean isPresent() {
+ return false;
+ }
+
+ /**
+ * Always throws a {@link IllegalStateException}.
+ */
+ @Override
+ public String getValue() {
+ throw createGetValueISE();
+ }
+
+ /**
+ * Always returns the value supplied by {@code defaultValueSupplier}.
+ */
+ @Override
+ public String or(Supplier<String> defaultValueSupplier) {
+ return checkDefaultValueSupplier(defaultValueSupplier).get();
+ }
+
+ /**
+ * Returns itself.
+ */
+ @Override
+ public StringParam emptyAsNull() {
+ return this;
+ }
+ }
+
+ /**
+ * Implementation of {@link StringParam} where the param is present.
+ */
+ private static final class StringParamImpl implements StringParam {
+ @CheckForNull
+ private final String value;
+ private final boolean emptyAsNull;
+
+ private StringParamImpl(@Nullable String value, boolean emptyAsNull) {
+ this.value = value;
+ this.emptyAsNull = emptyAsNull;
+ }
+
+ static StringParam present(String value) {
+ return new StringParamImpl(value, false);
+ }
+
+ @Override
+ public boolean isPresent() {
+ return true;
+ }
+
+ @Override
+ public String getValue() {
+ if (emptyAsNull && value != null && value.isEmpty()) {
+ return null;
+ }
+ return value;
+ }
+
+ @Override
+ @CheckForNull
+ public String or(Supplier<String> defaultValueSupplier) {
+ checkDefaultValueSupplier(defaultValueSupplier);
+ if (emptyAsNull && value != null && value.isEmpty()) {
+ return null;
+ }
+ return value;
+ }
+
+ @Override
+ public StringParam emptyAsNull() {
+ if (emptyAsNull || (value != null && !value.isEmpty())) {
+ return this;
+ }
+ return new StringParamImpl(value, true);
+ }
+ }
+
+ private static <T> Supplier<T> checkDefaultValueSupplier(Supplier<T> defaultValueSupplier) {
+ return requireNonNull(defaultValueSupplier, "default value supplier can't be null");
+ }
+
+ private static IllegalStateException createGetValueISE() {
+ return new IllegalStateException("Param has no value. Use isPresent() before calling getValue()");
+ }
}
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.Nullable;
import org.apache.commons.io.IOUtils;
import org.junit.Before;
import static com.google.common.collect.Lists.newArrayList;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.fail;
import static org.mockito.Mockito.mock;
import static org.sonar.api.utils.DateUtils.parseDate;
import static org.sonar.api.utils.DateUtils.parseDateTime;
assertThat(underTest.setParam("a_date", "2014-05-27").paramAsDate("a_date")).isEqualTo(DateUtils.parseDate("2014-05-27"));
}
+ @Test
+ public void getParam_of_missing_string_parameter() {
+ Request.StringParam stringParam = underTest.getParam("boo");
+
+ assertThat(stringParam.isPresent()).isFalse();
+ expectSupplierCanNotBeNullNPE(() -> stringParam.or(null));
+ assertThat(stringParam.or(() -> "foo")).isEqualTo("foo");
+ expectGetValueFailureWithISE(stringParam::getValue);
+
+ Request.StringParam emptyAsNull = stringParam.emptyAsNull();
+ assertThat(emptyAsNull).isSameAs(stringParam);
+ assertThat(emptyAsNull.isPresent()).isFalse();
+ expectSupplierCanNotBeNullNPE(() -> emptyAsNull.or(null));
+ assertThat(emptyAsNull.or(() -> "bar")).isEqualTo("bar");
+ expectGetValueFailureWithISE(emptyAsNull::getValue);
+ }
+
+ @Test
+ public void getParam_of_existing_string_parameter_with_non_empty_value() {
+ underTest.setParam("a_string", "sorry");
+
+ Request.StringParam stringParam = underTest.getParam("a_string");
+
+ assertThat(stringParam.isPresent()).isTrue();
+ expectSupplierCanNotBeNullNPE(() -> stringParam.or(null));
+ assertThat(stringParam.or(() -> "foo")).isEqualTo("sorry");
+ assertThat(stringParam.getValue()).isEqualTo("sorry");
+
+ Request.StringParam emptyAsNull = stringParam.emptyAsNull();
+ assertThat(emptyAsNull).isSameAs(stringParam);
+ assertThat(emptyAsNull.isPresent()).isTrue();
+ expectSupplierCanNotBeNullNPE(() -> emptyAsNull.or(null));
+ assertThat(emptyAsNull.or(() -> "bar")).isEqualTo("sorry");
+ assertThat(emptyAsNull.getValue()).isEqualTo("sorry");
+ }
+
+ @Test
+ public void getParam_of_existing_string_parameter_with_empty_value() {
+ underTest.setParam("a_string", "");
+
+ Request.StringParam stringParam = underTest.getParam("a_string");
+
+ assertThat(stringParam.isPresent()).isTrue();
+ expectSupplierCanNotBeNullNPE(() -> stringParam.or(null));
+ assertThat(stringParam.or(() -> "foo")).isEqualTo("");
+ assertThat(stringParam.getValue()).isEqualTo("");
+
+ Request.StringParam emptyAsNull = stringParam.emptyAsNull();
+ assertThat(emptyAsNull).isNotSameAs(stringParam);
+ assertThat(emptyAsNull.isPresent()).isTrue();
+ expectSupplierCanNotBeNullNPE(() -> emptyAsNull.or(null));
+ assertThat(emptyAsNull.or(() -> "bar")).isEqualTo(null);
+ assertThat(emptyAsNull.getValue()).isEqualTo(null);
+ }
+
+ @Test
+ public void getParam_with_validation_of_missing_string_parameter() {
+ Request.StringParam stringParam = underTest.getParam("boo", (str) -> {
+ throw new IllegalStateException("validator should not be called");
+ });
+
+ assertThat(stringParam.isPresent()).isFalse();
+ expectSupplierCanNotBeNullNPE(() -> stringParam.or(null));
+ assertThat(stringParam.or(() -> "foo")).isEqualTo("foo");
+ expectGetValueFailureWithISE(stringParam::getValue);
+
+ Request.StringParam emptyAsNull = stringParam.emptyAsNull();
+ assertThat(emptyAsNull).isSameAs(stringParam);
+ assertThat(emptyAsNull.isPresent()).isFalse();
+ expectSupplierCanNotBeNullNPE(() -> emptyAsNull.or(null));
+ assertThat(emptyAsNull.or(() -> "bar")).isEqualTo("bar");
+ expectGetValueFailureWithISE(emptyAsNull::getValue);
+ }
+
+ @Test
+ public void getParam_with_validation_of_existing_string_parameter_with_non_empty_value() {
+ underTest.setParam("a_string", "sorry");
+ AtomicInteger calls = new AtomicInteger();
+
+ Request.StringParam stringParam = underTest.getParam("a_string", (str) -> calls.incrementAndGet());
+
+ assertThat(calls.get()).isEqualTo(1);
+ assertThat(stringParam.isPresent()).isTrue();
+ expectSupplierCanNotBeNullNPE(() -> stringParam.or(null));
+ assertThat(stringParam.or(() -> "foo")).isEqualTo("sorry");
+ assertThat(stringParam.getValue()).isEqualTo("sorry");
+
+ Request.StringParam emptyAsNull = stringParam.emptyAsNull();
+ assertThat(emptyAsNull).isSameAs(stringParam);
+ assertThat(emptyAsNull.isPresent()).isTrue();
+ expectSupplierCanNotBeNullNPE(() -> emptyAsNull.or(null));
+ assertThat(emptyAsNull.or(() -> "bar")).isEqualTo("sorry");
+ assertThat(emptyAsNull.getValue()).isEqualTo("sorry");
+ }
+
+ @Test
+ public void getParam_with_validation_of_existing_string_parameter_with_empty_value() {
+ underTest.setParam("a_string", "");
+ AtomicInteger calls = new AtomicInteger();
+
+ Request.StringParam stringParam = underTest.getParam("a_string", (str) -> calls.incrementAndGet());
+
+ assertThat(calls.get()).isEqualTo(1);
+ assertThat(stringParam.isPresent()).isTrue();
+ expectSupplierCanNotBeNullNPE(() -> stringParam.or(null));
+ assertThat(stringParam.or(() -> "foo")).isEqualTo("");
+ assertThat(stringParam.getValue()).isEqualTo("");
+
+ Request.StringParam emptyAsNull = stringParam.emptyAsNull();
+ assertThat(emptyAsNull).isNotSameAs(stringParam);
+ assertThat(emptyAsNull.isPresent()).isTrue();
+ expectSupplierCanNotBeNullNPE(() -> emptyAsNull.or(null));
+ assertThat(emptyAsNull.or(() -> "bar")).isEqualTo(null);
+ assertThat(emptyAsNull.getValue()).isEqualTo(null);
+ }
+
+ @Test
+ public void getParam_with_validation_of_existing_string_parameter_does_not_catch_unchecked_exception_throws_by_validator() {
+ underTest.setParam("a_string", "boo");
+ IllegalArgumentException expected = new IllegalArgumentException("Faking validation of parameter value failed");
+
+ try {
+ underTest.getParam("a_string", (str) -> {throw expected; });
+ fail("an IllegalStateException should have been raised");
+ } catch (IllegalArgumentException e) {
+ assertThat(e).isSameAs(expected);
+ }
+ }
+
+ @Test
+ public void getParam_of_missing_parameter_of_unspecified_type() {
+ Request.Param<Object> param = underTest.getParam("baa", (rqt, key) -> {
+ throw new IllegalStateException("retrieveAndValidate BiConsumer should not be called");
+ });
+
+ assertThat(param.isPresent()).isFalse();
+ expectSupplierCanNotBeNullNPE(() -> param.or(null));
+ assertThat(param.or(() -> "foo")).isEqualTo("foo");
+ expectGetValueFailureWithISE(param::getValue);
+ }
+
+ @Test
+ public void getParam_of_existing_parameter_of_unspecified_type_with_null_value() {
+ underTest.setParam("a_string", "value in fake request actually does not matter");
+
+ Request.Param<Object> param = underTest.getParam("a_string", (rqt, key) -> null);
+
+ assertThat(param.isPresent()).isTrue();
+ expectSupplierCanNotBeNullNPE(() -> param.or(null));
+ assertThat(param.or(() -> "foo")).isNull();
+ assertThat(param.getValue()).isNull();
+ }
+
+ @Test
+ public void getParam_of_existing_parameter_of_unspecified_type_with_empty_string() {
+ underTest.setParam("a_string", "value in fake request actually does not matter");
+
+ Request.Param<Object> param = underTest.getParam("a_string", (rqt, key) -> "");
+
+ assertThat(param.isPresent()).isTrue();
+ expectSupplierCanNotBeNullNPE(() -> param.or(null));
+ assertThat(param.or(() -> "foo")).isEqualTo("");
+ assertThat(param.getValue()).isEqualTo("");
+ }
+
+ @Test
+ public void getParam_of_existing_parameter_of_unspecified_type_with_object() {
+ underTest.setParam("a_string", "value in fake request actually does not matter");
+ Object value = new Object();
+ Request.Param<Object> param = underTest.getParam("a_string", (rqt, key) -> value);
+
+ assertThat(param.isPresent()).isTrue();
+ expectSupplierCanNotBeNullNPE(() -> param.or(null));
+ assertThat(param.or(() -> "foo")).isSameAs(value);
+ assertThat(param.getValue()).isSameAs(value);
+ }
+
+ @Test
+ public void getParam_of_existing_parameter_of_unspecified_type_does_not_catch_unchecked_exception_thrown_by_BiConsumer() {
+ underTest.setParam("a_string", "value in fake request actually does not matter");
+ RuntimeException expected = new RuntimeException("Faking BiConsumer throwing unchecked exception");
+
+ try {
+ underTest.getParam("a_string", (rqt, key) -> { throw expected; });
+ fail("an RuntimeException should have been raised");
+ } catch (RuntimeException e) {
+ assertThat(e).isSameAs(expected);
+ }
+ }
+
+ private void expectGetValueFailureWithISE(Runnable runnable) {
+ try {
+ runnable.run();
+ fail("An IllegalStateException should have been raised");
+ } catch (IllegalStateException e) {
+ assertThat(e).hasMessage("Param has no value. Use isPresent() before calling getValue()");
+ }
+ }
+
+ private void expectSupplierCanNotBeNullNPE(Runnable runnable) {
+ try {
+ runnable.run();
+ fail("A NullPointerException should have been raised");
+ } catch (NullPointerException e) {
+ assertThat(e).hasMessage("default value supplier can't be null");
+ }
+ }
+
@DataProvider
public static Object[][] date_times() {
return new Object[][] {