result =
- declaringType.descriptor()
- .getOneofs()
- .stream()
- .map(oneof -> new OneofDeclaration(oneof, declaringType))
- .collect(toImmutableSet());
- return result;
- }
-
/**
* Obtains the name of the {@code oneof} field.
*/
@@ -63,21 +48,16 @@ public FieldName name() {
}
/**
- * Obtains the name of an enum which represents cases of the {@code oneof} field.
- *
- * Such an enum should be nested in the declaring message class.
- *
- *
If the declaring message class name is {@code com.acme.cms.Customer} and the {@code oneof}
- * name is {@code auth_provider}, the resulting class name would be
- * {@code com.acme.cms.Customer$AuthProviderCase}.
- *
- * @return the case enum FQN
+ * Obtains the {@code oneof} descriptor.
+ */
+ public OneofDescriptor descriptor() {
+ return oneof;
+ }
+
+ /**
+ * Obtains the type containing this group.
*/
- public ClassName javaCaseEnum() {
- ClassName declaringClassName = declaringType.javaClassName();
- io.spine.code.gen.java.FieldName oneofName =
- io.spine.code.gen.java.FieldName.from(name());
- SimpleClassName enumName = SimpleClassName.create(format("%sCase", oneofName.capitalize()));
- return declaringClassName.withNested(enumName);
+ public MessageType declaringType() {
+ return declaringType;
}
}
diff --git a/base/src/main/java/io/spine/validate/ConstraintTranslator.java b/base/src/main/java/io/spine/validate/ConstraintTranslator.java
index 063b50d15d..26616f39d0 100644
--- a/base/src/main/java/io/spine/validate/ConstraintTranslator.java
+++ b/base/src/main/java/io/spine/validate/ConstraintTranslator.java
@@ -22,6 +22,7 @@
import io.spine.validate.option.DistinctConstraint;
import io.spine.validate.option.GoesConstraint;
+import io.spine.validate.option.IsRequiredConstraint;
import io.spine.validate.option.PatternConstraint;
import io.spine.validate.option.RangedConstraint;
import io.spine.validate.option.RequiredConstraint;
@@ -101,6 +102,14 @@ public interface ConstraintTranslator {
*/
void visitRequiredField(RequiredFieldConstraint constraint);
+ /**
+ * Translates the given {@link IsRequiredConstraint}.
+ *
+ * @param constraint
+ * the constraint of a oneof group
+ */
+ void visitRequiredOneof(IsRequiredConstraint constraint);
+
/**
* Translates the given {@link CustomConstraint}.
*
diff --git a/base/src/main/java/io/spine/validate/Constraints.java b/base/src/main/java/io/spine/validate/Constraints.java
index d854e46a54..49c8fa2360 100644
--- a/base/src/main/java/io/spine/validate/Constraints.java
+++ b/base/src/main/java/io/spine/validate/Constraints.java
@@ -23,7 +23,9 @@
import com.google.common.collect.ImmutableList;
import com.google.errorprone.annotations.Immutable;
import io.spine.code.proto.FieldContext;
+import io.spine.code.proto.OneofDeclaration;
import io.spine.type.MessageType;
+import io.spine.validate.option.IsRequired;
import io.spine.validate.option.RequiredField;
import static com.google.common.base.Preconditions.checkNotNull;
@@ -77,6 +79,7 @@ static Constraints loadFor(MessageType type, FieldContext context) {
.flatMap(FieldConstraints::of)
.forEach(constraintBuilder::add);
addRequiredField(type, constraintBuilder);
+ scanIsRequired(type, constraintBuilder);
return new Constraints(constraintBuilder.build());
}
@@ -89,6 +92,18 @@ private static void addRequiredField(MessageType type,
}
}
+ private static void scanIsRequired(MessageType type,
+ ImmutableList.Builder builder) {
+ IsRequired option = new IsRequired();
+ type.descriptor()
+ .getOneofs()
+ .stream()
+ .filter(option::valuePresent)
+ .map(descriptor -> new OneofDeclaration(descriptor, type))
+ .map(option::constraintFor)
+ .forEach(builder::add);
+ }
+
/**
* Assembles non-standard constraints from the given message type in the given field context.
*/
diff --git a/base/src/main/java/io/spine/validate/MessageValidator.java b/base/src/main/java/io/spine/validate/MessageValidator.java
index 9454aa8de5..1e2d163847 100644
--- a/base/src/main/java/io/spine/validate/MessageValidator.java
+++ b/base/src/main/java/io/spine/validate/MessageValidator.java
@@ -24,6 +24,7 @@
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Range;
import com.google.protobuf.Message;
+import io.spine.base.Field;
import io.spine.base.FieldPath;
import io.spine.code.proto.FieldContext;
import io.spine.code.proto.FieldDeclaration;
@@ -32,6 +33,7 @@
import io.spine.type.TypeName;
import io.spine.validate.option.DistinctConstraint;
import io.spine.validate.option.GoesConstraint;
+import io.spine.validate.option.IsRequiredConstraint;
import io.spine.validate.option.PatternConstraint;
import io.spine.validate.option.RangedConstraint;
import io.spine.validate.option.RequiredConstraint;
@@ -190,6 +192,22 @@ public void visitRequiredField(RequiredFieldConstraint constraint) {
violation.ifPresent(violations::add);
}
+ @Override
+ public void visitRequiredOneof(IsRequiredConstraint constraint) {
+ Optional fieldValue = message.valueOf(constraint.declaration());
+ boolean noneSet = !fieldValue.isPresent();
+ if (noneSet) {
+ MessageType targetType = constraint.targetType();
+ ConstraintViolation violation = ConstraintViolation
+ .newBuilder()
+ .setMsgFormat(constraint.errorMessage(message.context()))
+ .setFieldPath(Field.named(constraint.oneofName().value()).path())
+ .setTypeName(targetType.name().value())
+ .build();
+ violations.add(violation);
+ }
+ }
+
@Override
public void visitCustom(CustomConstraint constraint) {
ImmutableList violations = constraint.validate(message);
diff --git a/base/src/main/java/io/spine/validate/MessageValue.java b/base/src/main/java/io/spine/validate/MessageValue.java
index 5dda308d6a..d36eca58b0 100644
--- a/base/src/main/java/io/spine/validate/MessageValue.java
+++ b/base/src/main/java/io/spine/validate/MessageValue.java
@@ -27,6 +27,7 @@
import com.google.protobuf.Message;
import io.spine.code.proto.FieldContext;
import io.spine.code.proto.FieldDeclaration;
+import io.spine.code.proto.OneofDeclaration;
import io.spine.type.MessageType;
import org.checkerframework.checker.nullness.qual.Nullable;
@@ -147,10 +148,10 @@ public FieldValue valueOf(FieldDeclaration field) {
}
/**
- * Obtains the value of a populated {@code Oneof} field.
+ * Obtains the value of a populated {@code oneof} field.
*
* @param oneof
- * the {@code Oneof} descriptor
+ * the {@code oneof} descriptor
* @return a value of the populated field
* or {@code Optional.empty()} if the field was not populated
* @throws IllegalArgumentException
@@ -163,6 +164,20 @@ public Optional valueOf(OneofDescriptor oneof) {
return valueOfNullable(field);
}
+ /**
+ * Obtains the value of a populated {@code oneof} field.
+ *
+ * @param oneof
+ * the {@code oneof} declaration
+ * @return a value of the populated field
+ * or {@code Optional.empty()} if the field was not populated
+ * @throws IllegalArgumentException
+ * if the if the message doesn't declare this oneof
+ */
+ public Optional valueOf(OneofDeclaration oneof) {
+ return valueOf(oneof.descriptor());
+ }
+
/** Returns the context of the message. */
@SuppressWarnings("unused")
FieldContext context() {
@@ -188,5 +203,4 @@ private FieldValue valueOfField(FieldDescriptor field) {
private Object readValue(FieldDescriptor field) {
return asFieldAware == null ? message.getField(field) : asFieldAware.readValue(field);
}
-
}
diff --git a/base/src/test/java/io/spine/validate/builders/package-info.java b/base/src/main/java/io/spine/validate/option/IsRequired.java
similarity index 50%
rename from base/src/test/java/io/spine/validate/builders/package-info.java
rename to base/src/main/java/io/spine/validate/option/IsRequired.java
index 3163420de0..10e1ebd6c0 100644
--- a/base/src/test/java/io/spine/validate/builders/package-info.java
+++ b/base/src/main/java/io/spine/validate/option/IsRequired.java
@@ -18,14 +18,34 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+package io.spine.validate.option;
+
+import com.google.errorprone.annotations.Immutable;
+import com.google.protobuf.Descriptors.OneofDescriptor;
+import io.spine.code.proto.OneofDeclaration;
+import io.spine.validate.Constraint;
+
+import java.util.Optional;
+
+import static io.spine.option.OptionsProto.isRequired;
+
/**
- * Tests of validating builders are placed into this package
- * to simulate usage of the public API.
+ * A {@code oneof} validation option which constrains the target {@code oneof} group to be set.
+ *
+ * If the value of the option is {@code true}, one of the fields in the group must be set.
*/
-@CheckReturnValue
-@ParametersAreNonnullByDefault
-package io.spine.validate.builders;
+@Immutable
+public class IsRequired implements ValidatingOption {
-import com.google.errorprone.annotations.CheckReturnValue;
+ @Override
+ public Constraint constraintFor(OneofDeclaration field) {
+ return new IsRequiredConstraint(field);
+ }
-import javax.annotation.ParametersAreNonnullByDefault;
+ @Override
+ public Optional valueFrom(OneofDescriptor descriptor) {
+ boolean value = descriptor.getOptions()
+ .getExtension(isRequired);
+ return value ? Optional.of(true) : Optional.empty();
+ }
+}
diff --git a/base/src/main/java/io/spine/validate/option/IsRequiredConstraint.java b/base/src/main/java/io/spine/validate/option/IsRequiredConstraint.java
new file mode 100644
index 0000000000..8944a91c94
--- /dev/null
+++ b/base/src/main/java/io/spine/validate/option/IsRequiredConstraint.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2020, TeamDev. All rights reserved.
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package io.spine.validate.option;
+
+import com.google.errorprone.annotations.Immutable;
+import io.spine.code.proto.FieldContext;
+import io.spine.code.proto.FieldName;
+import io.spine.code.proto.OneofDeclaration;
+import io.spine.type.MessageType;
+import io.spine.validate.Constraint;
+import io.spine.validate.ConstraintTranslator;
+
+/**
+ * A {@code oneof} group constraint which signifies that one of the fields must be set.
+ */
+@Immutable
+public final class IsRequiredConstraint implements Constraint {
+
+ private final OneofDeclaration declaration;
+
+ IsRequiredConstraint(OneofDeclaration declaration) {
+ this.declaration = declaration;
+ }
+
+ @Override
+ public MessageType targetType() {
+ return declaration.declaringType();
+ }
+
+ @Override
+ public String errorMessage(FieldContext field) {
+ return String.format("One of fields in group `%s` must be set.", declaration.name());
+ }
+
+ @Override
+ public void accept(ConstraintTranslator> visitor) {
+ visitor.visitRequiredOneof(this);
+ }
+
+ /**
+ * Obtains the name of the {@code oneof} group.
+ */
+ public FieldName oneofName() {
+ return declaration.name();
+ }
+
+ /**
+ * Obtains the {@code oneof} declaration.
+ */
+ public OneofDeclaration declaration() {
+ return declaration;
+ }
+}
diff --git a/base/src/main/java/io/spine/validate/option/RequiredFieldConstraint.java b/base/src/main/java/io/spine/validate/option/RequiredFieldConstraint.java
index 7f6b9637d3..a6509d7b0c 100644
--- a/base/src/main/java/io/spine/validate/option/RequiredFieldConstraint.java
+++ b/base/src/main/java/io/spine/validate/option/RequiredFieldConstraint.java
@@ -64,11 +64,19 @@ public void accept(ConstraintTranslator> visitor) {
visitor.visitRequiredField(this);
}
+ /**
+ * Obtains the raw option value.
+ */
public String optionValue() {
return optionValue;
}
- public ImmutableSet alternatives() {
+ /**
+ * Obtains the set of alternative conjunctive field expressions.
+ *
+ * @see Alternative
+ */
+ public ImmutableSet alternatives() {
return alternatives;
}
}
diff --git a/base/src/main/proto/spine/options.proto b/base/src/main/proto/spine/options.proto
index 4324ed9127..4b29b3466f 100644
--- a/base/src/main/proto/spine/options.proto
+++ b/base/src/main/proto/spine/options.proto
@@ -261,11 +261,23 @@ extend google.protobuf.FieldOptions {
//
bool column = 73854;
- // Reserved 73855 to 73899 for future options.
+ // Reserved 73855 to 73890 for future options.
// Reserved 73900 for removed `by` option.
}
+extend google.protobuf.OneofOptions {
+
+ // Marks a `oneof` group, in which one field *must* be set.
+ //
+ // Alternative to `(required_field)` with all the field in the group joined with the OR
+ // operator.
+ //
+ bool is_required = 73891;
+
+ // Reserved 73892 to 73899 for future options.
+}
+
extend google.protobuf.MessageOptions {
// Validation Constraints
@@ -282,7 +294,7 @@ extend google.protobuf.MessageOptions {
//
// Unlike the `required` field constraint which always require corresponding field,
// this message option allows to require alternative fields or a combination of them as
- // an alternative.
+ // an alternative. Field names and `oneof` group names are acceptable.
//
// Field names are separated using the pipe (`|`) symbol. The combination of fields is defined
// using the ampersand (`&`) symbol.
diff --git a/tools/tool-base/src/test/java/io/spine/code/gen/java/OneofDeclarationTest.java b/base/src/test/java/io/spine/code/proto/OneofDeclarationTest.java
similarity index 91%
rename from tools/tool-base/src/test/java/io/spine/code/gen/java/OneofDeclarationTest.java
rename to base/src/test/java/io/spine/code/proto/OneofDeclarationTest.java
index aa2b026601..e01073dfb7 100644
--- a/tools/tool-base/src/test/java/io/spine/code/gen/java/OneofDeclarationTest.java
+++ b/base/src/test/java/io/spine/code/proto/OneofDeclarationTest.java
@@ -18,13 +18,12 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package io.spine.code.gen.java;
+package io.spine.code.proto;
import com.google.common.testing.NullPointerTester;
import com.google.protobuf.Descriptors;
import com.google.protobuf.Empty;
-import io.spine.code.proto.FieldName;
-import io.spine.test.code.generate.Transmission;
+import io.spine.test.type.Transmission;
import io.spine.type.MessageType;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@@ -47,8 +46,6 @@ void nonNull() {
@Test
@DisplayName("obtain name")
- @SuppressWarnings("DuplicateStringLiteralInspection")
- // "type" is also used in generated code.
void obtainName() {
Descriptors.Descriptor declaringType = Transmission.getDescriptor();
Descriptors.OneofDescriptor protocolOneof = declaringType
diff --git a/base/src/test/java/io/spine/validate/option/IsRequiredTest.java b/base/src/test/java/io/spine/validate/option/IsRequiredTest.java
new file mode 100644
index 0000000000..50fcd2c17a
--- /dev/null
+++ b/base/src/test/java/io/spine/validate/option/IsRequiredTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2020, TeamDev. All rights reserved.
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package io.spine.validate.option;
+
+import io.spine.test.validate.Fish;
+import io.spine.test.validate.Meal;
+import io.spine.test.validate.Sauce;
+import io.spine.validate.ValidationOfConstraintTest;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import static com.google.common.truth.Truth.assertThat;
+import static io.spine.base.Identifier.newUuid;
+import static io.spine.validate.ValidationOfConstraintTest.VALIDATION_SHOULD;
+
+@DisplayName(VALIDATION_SHOULD + "analyze (is_required) oneof option and")
+class IsRequiredTest extends ValidationOfConstraintTest {
+
+ @Test
+ @DisplayName("throw if required field group is not set")
+ void required() {
+ Meal message = Meal
+ .newBuilder()
+ .setCheese(Sauce.getDefaultInstance())
+ .buildPartial();
+ validate(message);
+ assertThat(firstViolation().getFieldPath().getFieldName(0))
+ .isEqualTo("choice");
+ }
+
+ @Test
+ @DisplayName("not throw if required field group is set")
+ void requiredSet() {
+ Fish fish = Fish
+ .newBuilder()
+ .setDescription(newUuid())
+ .build();
+ Meal message = Meal
+ .newBuilder()
+ .setCheese(Sauce.getDefaultInstance())
+ .setFish(fish)
+ .buildPartial();
+ assertValid(message);
+ }
+
+ @Test
+ @DisplayName("ignore non-required field groups")
+ void notRequired() {
+ Fish fish = Fish
+ .newBuilder()
+ .setDescription(newUuid())
+ .build();
+ Meal message = Meal
+ .newBuilder()
+ .setFish(fish)
+ .buildPartial();
+ assertValid(message);
+ }
+}
diff --git a/tools/tool-base/src/test/proto/spine/test/code/generate/oneof_declaration_test.proto b/base/src/test/proto/spine/test/type/oneof_declaration_test.proto
similarity index 96%
rename from tools/tool-base/src/test/proto/spine/test/code/generate/oneof_declaration_test.proto
rename to base/src/test/proto/spine/test/type/oneof_declaration_test.proto
index ecde505d39..ad0395396a 100644
--- a/tools/tool-base/src/test/proto/spine/test/code/generate/oneof_declaration_test.proto
+++ b/base/src/test/proto/spine/test/type/oneof_declaration_test.proto
@@ -25,7 +25,7 @@ package spine.test.code.generate;
import "spine/options.proto";
option (type_url_prefix) = "type.spine.io";
-option java_package = "io.spine.test.code.generate";
+option java_package = "io.spine.test.type";
option java_outer_classname = "OneofDeclarationTestProto";
option java_multiple_files = true;
diff --git a/base/src/test/proto/spine/test/validate/is_required_test.proto b/base/src/test/proto/spine/test/validate/is_required_test.proto
new file mode 100644
index 0000000000..9599f0341e
--- /dev/null
+++ b/base/src/test/proto/spine/test/validate/is_required_test.proto
@@ -0,0 +1,44 @@
+syntax = "proto3";
+
+package spine.test.validate;
+
+import "spine/options.proto";
+
+option (type_url_prefix) = "type.spine.io";
+option java_package = "io.spine.test.validate";
+option java_outer_classname = "RequiredFieldsTestProto";
+option java_multiple_files = true;
+
+message Meal {
+
+ oneof choice {
+ option (is_required) = true;
+
+ Fish fish = 1;
+ Meat meat = 2;
+ Vegetables veggies = 3;
+ }
+
+ oneof sauce {
+ option (is_required) = false;
+
+ Sauce ketchup = 4;
+ Sauce cheese = 5;
+ }
+}
+
+message Fish {
+ string description = 1;
+}
+
+message Meat {
+ string description = 1;
+}
+
+message Vegetables {
+ string description = 1;
+}
+
+message Sauce {
+ string description = 1;
+}
diff --git a/config b/config
index 63701feb02..08fefceea8 160000
--- a/config
+++ b/config
@@ -1 +1 @@
-Subproject commit 63701feb02a70c6af1e50aa2f0ca812bdab3c233
+Subproject commit 08fefceea80948d89b05d3f8ad58c7fe8cbe57c6
diff --git a/license-report.md b/license-report.md
index 3a8acadd71..1e5d2392a5 100644
--- a/license-report.md
+++ b/license-report.md
@@ -1,6 +1,6 @@
-# Dependencies of `io.spine:spine-base:1.5.3`
+# Dependencies of `io.spine:spine-base:1.5.4`
## Runtime
1. **Group:** com.google.code.findbugs **Name:** jsr305 **Version:** 3.0.2
@@ -330,12 +330,12 @@
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Wed Apr 01 23:07:51 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
+This report was generated on **Mon Apr 06 12:01:12 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
-# Dependencies of `io.spine.tools:spine-errorprone-checks:1.5.3`
+# Dependencies of `io.spine.tools:spine-errorprone-checks:1.5.4`
## Runtime
1. **Group:** com.github.ben-manes.caffeine **Name:** caffeine **Version:** 2.7.0
@@ -777,12 +777,12 @@ This report was generated on **Wed Apr 01 23:07:51 EEST 2020** using [Gradle-Lic
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Wed Apr 01 23:07:52 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
+This report was generated on **Mon Apr 06 12:01:13 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
-# Dependencies of `io.spine.tools:spine-javadoc-filter:1.5.3`
+# Dependencies of `io.spine.tools:spine-javadoc-filter:1.5.4`
## Runtime
1. **Group:** com.google.android **Name:** annotations **Version:** 4.1.1.4
@@ -1170,12 +1170,12 @@ This report was generated on **Wed Apr 01 23:07:52 EEST 2020** using [Gradle-Lic
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Wed Apr 01 23:07:53 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
+This report was generated on **Mon Apr 06 12:01:14 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
-# Dependencies of `io.spine.tools:spine-javadoc-prettifier:1.5.3`
+# Dependencies of `io.spine.tools:spine-javadoc-prettifier:1.5.4`
## Runtime
1. **Group:** com.google.code.findbugs **Name:** jsr305 **Version:** 3.0.2
@@ -1537,12 +1537,12 @@ This report was generated on **Wed Apr 01 23:07:53 EEST 2020** using [Gradle-Lic
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Wed Apr 01 23:07:53 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
+This report was generated on **Mon Apr 06 12:01:14 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
-# Dependencies of `io.spine.tools:spine-model-compiler:1.5.3`
+# Dependencies of `io.spine.tools:spine-model-compiler:1.5.4`
## Runtime
1. **Group:** com.google.code.findbugs **Name:** jsr305 **Version:** 3.0.2
@@ -1920,12 +1920,12 @@ This report was generated on **Wed Apr 01 23:07:53 EEST 2020** using [Gradle-Lic
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Wed Apr 01 23:07:53 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
+This report was generated on **Mon Apr 06 12:01:15 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
-# Dependencies of `io.spine.tools:spine-mute-logging:1.5.3`
+# Dependencies of `io.spine.tools:spine-mute-logging:1.5.4`
## Runtime
1. **Group:** com.google.auto.value **Name:** auto-value-annotations **Version:** 1.6.3
@@ -2305,12 +2305,12 @@ This report was generated on **Wed Apr 01 23:07:53 EEST 2020** using [Gradle-Lic
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Wed Apr 01 23:07:53 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
+This report was generated on **Mon Apr 06 12:01:15 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
-# Dependencies of `io.spine.tools:spine-plugin-base:1.5.3`
+# Dependencies of `io.spine.tools:spine-plugin-base:1.5.4`
## Runtime
1. **Group:** com.google.code.findbugs **Name:** jsr305 **Version:** 3.0.2
@@ -2672,12 +2672,12 @@ This report was generated on **Wed Apr 01 23:07:53 EEST 2020** using [Gradle-Lic
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Wed Apr 01 23:07:54 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
+This report was generated on **Mon Apr 06 12:01:16 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
-# Dependencies of `io.spine.tools:spine-plugin-testlib:1.5.3`
+# Dependencies of `io.spine.tools:spine-plugin-testlib:1.5.4`
## Runtime
1. **Group:** com.google.auto.value **Name:** auto-value-annotations **Version:** 1.6.3
@@ -3097,12 +3097,12 @@ This report was generated on **Wed Apr 01 23:07:54 EEST 2020** using [Gradle-Lic
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Wed Apr 01 23:07:54 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
+This report was generated on **Mon Apr 06 12:01:16 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
-# Dependencies of `io.spine.tools:spine-proto-dart-plugin:1.5.3`
+# Dependencies of `io.spine.tools:spine-proto-dart-plugin:1.5.4`
## Runtime
1. **Group:** com.google.code.findbugs **Name:** jsr305 **Version:** 3.0.2
@@ -3464,12 +3464,12 @@ This report was generated on **Wed Apr 01 23:07:54 EEST 2020** using [Gradle-Lic
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Wed Apr 01 23:07:54 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
+This report was generated on **Mon Apr 06 12:01:17 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
-# Dependencies of `io.spine.tools:spine-proto-js-plugin:1.5.3`
+# Dependencies of `io.spine.tools:spine-proto-js-plugin:1.5.4`
## Runtime
1. **Group:** com.google.code.findbugs **Name:** jsr305 **Version:** 3.0.2
@@ -3831,12 +3831,12 @@ This report was generated on **Wed Apr 01 23:07:54 EEST 2020** using [Gradle-Lic
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Wed Apr 01 23:07:54 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
+This report was generated on **Mon Apr 06 12:01:17 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
-# Dependencies of `io.spine.tools:spine-protoc-api:1.5.3`
+# Dependencies of `io.spine.tools:spine-protoc-api:1.5.4`
## Runtime
1. **Group:** com.google.code.findbugs **Name:** jsr305 **Version:** 3.0.2
@@ -4158,12 +4158,12 @@ This report was generated on **Wed Apr 01 23:07:54 EEST 2020** using [Gradle-Lic
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Wed Apr 01 23:07:55 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
+This report was generated on **Mon Apr 06 12:01:18 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
-# Dependencies of `io.spine.tools:spine-protoc-plugin:1.5.3`
+# Dependencies of `io.spine.tools:spine-protoc-plugin:1.5.4`
## Runtime
1. **Group:** com.google.code.findbugs **Name:** jsr305 **Version:** 3.0.2
@@ -4493,12 +4493,12 @@ This report was generated on **Wed Apr 01 23:07:55 EEST 2020** using [Gradle-Lic
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Wed Apr 01 23:07:55 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
+This report was generated on **Mon Apr 06 12:01:18 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
-# Dependencies of `io.spine:spine-testlib:1.5.3`
+# Dependencies of `io.spine:spine-testlib:1.5.4`
## Runtime
1. **Group:** com.google.auto.value **Name:** auto-value-annotations **Version:** 1.6.3
@@ -4878,12 +4878,12 @@ This report was generated on **Wed Apr 01 23:07:55 EEST 2020** using [Gradle-Lic
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Wed Apr 01 23:07:55 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
+This report was generated on **Mon Apr 06 12:01:18 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
-# Dependencies of `io.spine.tools:spine-tool-base:1.5.3`
+# Dependencies of `io.spine.tools:spine-tool-base:1.5.4`
## Runtime
1. **Group:** com.google.code.findbugs **Name:** jsr305 **Version:** 3.0.2
@@ -5213,12 +5213,12 @@ This report was generated on **Wed Apr 01 23:07:55 EEST 2020** using [Gradle-Lic
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Wed Apr 01 23:07:55 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
+This report was generated on **Mon Apr 06 12:01:19 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
-# Dependencies of `io.spine.tools:spine-validation-generator:1.5.3`
+# Dependencies of `io.spine.tools:spine-validation-generator:1.5.4`
## Runtime
1. **Group:** com.google.code.findbugs **Name:** jsr305 **Version:** 3.0.2
@@ -5548,4 +5548,4 @@ This report was generated on **Wed Apr 01 23:07:55 EEST 2020** using [Gradle-Lic
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Wed Apr 01 23:07:55 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
\ No newline at end of file
+This report was generated on **Mon Apr 06 12:01:19 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 77b0fb3b1d..a53084ee50 100644
--- a/pom.xml
+++ b/pom.xml
@@ -12,7 +12,7 @@ all modules and does not describe the project structure per-subproject.
io.spine
spine-base
-1.5.3
+1.5.4
2015
@@ -154,7 +154,7 @@ all modules and does not describe the project structure per-subproject.
io.spine.tools
spine-protoc-plugin
- 1.5.3
+ 1.5.4
test
diff --git a/tools/smoke-tests/generated-validation-tests/src/test/java/io/spine/test/tools/validate/IsRequiredTest.java b/tools/smoke-tests/generated-validation-tests/src/test/java/io/spine/test/tools/validate/IsRequiredTest.java
new file mode 100644
index 0000000000..d8c3c1bd61
--- /dev/null
+++ b/tools/smoke-tests/generated-validation-tests/src/test/java/io/spine/test/tools/validate/IsRequiredTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2020, TeamDev. All rights reserved.
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package io.spine.test.tools.validate;
+
+import com.google.protobuf.Message;
+import io.spine.validate.ConstraintViolation;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+import static com.google.common.truth.Truth.assertThat;
+import static io.spine.base.Identifier.newUuid;
+import static io.spine.validate.Validate.violationsOf;
+
+@DisplayName("`(is_required)` constraint should be compiled so that")
+class IsRequiredTest {
+
+ @Test
+ @DisplayName("throw if required field group is not set")
+ void required() {
+ Meal message = Meal
+ .newBuilder()
+ .setCheese(Sauce.getDefaultInstance())
+ .buildPartial();
+ List violations = violationsOf(message);
+ assertThat(violations)
+ .hasSize(1);
+ assertThat(violations.get(0).getMsgFormat())
+ .contains("choice");
+ }
+
+ @Test
+ @DisplayName("not throw if required field group is set")
+ void requiredSet() {
+ Fish fish = Fish
+ .newBuilder()
+ .setDescription(newUuid())
+ .build();
+ Meal message = Meal
+ .newBuilder()
+ .setCheese(Sauce.getDefaultInstance())
+ .setFish(fish)
+ .buildPartial();
+ assertValid(message);
+ }
+
+ @Test
+ @DisplayName("ignore non-required field groups")
+ void notRequired() {
+ Fish fish = Fish
+ .newBuilder()
+ .setDescription(newUuid())
+ .build();
+ Meal message = Meal
+ .newBuilder()
+ .setFish(fish)
+ .buildPartial();
+ assertValid(message);
+ }
+
+ private static void assertValid(Message message) {
+ assertThat(violationsOf(message))
+ .isEmpty();
+ }
+}
diff --git a/tools/smoke-tests/generated-validation-tests/src/test/proto/spine/test/tools/validate/is_required.proto b/tools/smoke-tests/generated-validation-tests/src/test/proto/spine/test/tools/validate/is_required.proto
new file mode 100644
index 0000000000..a9ead48b44
--- /dev/null
+++ b/tools/smoke-tests/generated-validation-tests/src/test/proto/spine/test/tools/validate/is_required.proto
@@ -0,0 +1,44 @@
+syntax = "proto3";
+
+package spine.test.tools.validate;
+
+import "spine/options.proto";
+
+option (type_url_prefix) = "type.spine.io";
+option java_package = "io.spine.test.tools.validate";
+option java_outer_classname = "RequiredFieldsProto";
+option java_multiple_files = true;
+
+message Meal {
+
+ oneof choice {
+ option (is_required) = true;
+
+ Fish fish = 1;
+ Meat meat = 2;
+ Vegetables veggies = 3;
+ }
+
+ oneof sauce {
+ option (is_required) = false;
+
+ Sauce ketchup = 4;
+ Sauce cheese = 5;
+ }
+}
+
+message Fish {
+ string description = 1;
+}
+
+message Meat {
+ string description = 1;
+}
+
+message Vegetables {
+ string description = 1;
+}
+
+message Sauce {
+ string description = 1;
+}
diff --git a/tools/validation-generator/src/main/java/io/spine/tools/validate/MessageAccess.java b/tools/validation-generator/src/main/java/io/spine/tools/validate/MessageAccess.java
index 68b81581a7..63144ac9b1 100644
--- a/tools/validation-generator/src/main/java/io/spine/tools/validate/MessageAccess.java
+++ b/tools/validation-generator/src/main/java/io/spine/tools/validate/MessageAccess.java
@@ -21,11 +21,15 @@
package io.spine.tools.validate;
import com.google.protobuf.Message;
+import com.google.protobuf.ProtocolMessageEnum;
import io.spine.code.proto.FieldDeclaration;
+import io.spine.code.proto.OneofDeclaration;
import io.spine.tools.validate.code.CodeExpression;
+import io.spine.tools.validate.code.Expression;
import static com.google.common.base.Preconditions.checkNotNull;
import static io.spine.tools.validate.FieldAccess.fieldOfMessage;
+import static io.spine.tools.validate.code.Expression.formatted;
/**
* An expression which yields a message.
@@ -49,4 +53,13 @@ public static MessageAccess of(String value) {
public FieldAccess get(FieldDeclaration field) {
return fieldOfMessage(this, field);
}
+
+ /**
+ * Builds an expression which yields the {@code oneof} case for the given {@code oneof}.
+ *
+ * The case is represented by an enum value. See the Protobuf doc for more info on the enum.
+ */
+ public Expression oneofCase(OneofDeclaration declaration) {
+ return formatted("%s.get%sCase()", this, declaration.name().toCamelCase());
+ }
}
diff --git a/tools/validation-generator/src/main/java/io/spine/tools/validate/ValidationCodeGenerator.java b/tools/validation-generator/src/main/java/io/spine/tools/validate/ValidationCodeGenerator.java
index fba1158406..9b425383a5 100644
--- a/tools/validation-generator/src/main/java/io/spine/tools/validate/ValidationCodeGenerator.java
+++ b/tools/validation-generator/src/main/java/io/spine/tools/validate/ValidationCodeGenerator.java
@@ -24,6 +24,7 @@
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Range;
import com.google.common.reflect.TypeToken;
+import com.google.protobuf.ProtocolMessageEnum;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import io.spine.code.proto.FieldContext;
@@ -50,6 +51,7 @@
import io.spine.validate.option.DistinctConstraint;
import io.spine.validate.option.GoesConstraint;
import io.spine.validate.option.IfInvalid;
+import io.spine.validate.option.IsRequiredConstraint;
import io.spine.validate.option.PatternConstraint;
import io.spine.validate.option.RangedConstraint;
import io.spine.validate.option.RequiredConstraint;
@@ -333,6 +335,23 @@ public void visitRequiredField(RequiredFieldConstraint constraint) {
compiledConstraints.add(check);
}
+ @Override
+ public void visitRequiredOneof(IsRequiredConstraint constraint) {
+ Expression caseValue =
+ messageAccess.oneofCase(constraint.declaration());
+ BooleanExpression condition = fromCode("$L.getNumber() == $L", caseValue, 0);
+ Expression violation = NewViolation
+ .forMessage(fieldContext, type)
+ .setMessage(constraint.errorMessage(fieldContext))
+ .setField(fieldContext.fieldPath())
+ .build();
+ CodeBlock check = condition.ifTrue(violationAccumulator
+ .apply(violation)
+ .toCode())
+ .toCode();
+ compiledConstraints.add(check);
+ }
+
@Override
public void visitCustom(CustomConstraint constraint) {
throw unsupported(
diff --git a/tools/validation-generator/src/test/proto/spine/test/validate/avocado_farm.proto b/tools/validation-generator/src/test/proto/spine/test/validate/avocado_farm.proto
index b51313b968..b9b3b4756f 100644
--- a/tools/validation-generator/src/test/proto/spine/test/validate/avocado_farm.proto
+++ b/tools/validation-generator/src/test/proto/spine/test/validate/avocado_farm.proto
@@ -27,6 +27,14 @@ message Greenhouse {
HumidityRange humidity = 5 [(required) = true, (.validate) = true];
repeated Sort sort = 6 [(required) = false, (distinct) = true];
+
+ oneof lighting {
+ option (is_required) = true;
+
+ LightBulb incandescent = 7 [deprecated = true];
+
+ UvLightStrip uv = 8;
+ }
}
message Sort {
@@ -56,3 +64,12 @@ message HumidityRange {
Humidity highest_allowed = 2 [(required) = true, (.validate) = true];
}
+
+message LightBulb {
+ uint32 wattage = 1 [(min).value = "1"];
+}
+
+message UvLightStrip {
+ float min_wave_length = 1;
+ float max_wave_length = 2;
+}
diff --git a/version.gradle b/version.gradle
index 803e8d3b1a..8d3d59d0b5 100644
--- a/version.gradle
+++ b/version.gradle
@@ -25,7 +25,7 @@
* as we want to manage the versions in a single source.
*/
-final def SPINE_VERSION = '1.5.3'
+final def SPINE_VERSION = '1.5.4'
ext {
spineVersion = SPINE_VERSION