Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate Java validation code at compile time #497

Merged
merged 230 commits into from
Dec 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
230 commits
Select commit Hold shift + click to select a range
6a9764f
Add validation-generator module
dmdashenkov Nov 7, 2019
7534b21
Update naming
dmdashenkov Nov 8, 2019
ed3672f
Declare validator generator protoc plugin
dmdashenkov Nov 8, 2019
accf9ad
Fix protoc tests
dmdashenkov Nov 8, 2019
e305207
Make validator constructors private
dmdashenkov Nov 11, 2019
c602b9e
Merge branch 'master' into generate-java-validation
dmdashenkov Nov 12, 2019
65b36d7
Generate Kotlin code for validators
dmdashenkov Nov 12, 2019
a6c7468
Generate Kotlin validation code as an alternative to runtime validation
dmdashenkov Nov 12, 2019
1ce46e4
Make generated Kotlin validation classes private
dmdashenkov Nov 13, 2019
a2b5f3c
Fallback to Java code generation
dmdashenkov Nov 20, 2019
6fc237e
Clean up generation plugin
dmdashenkov Nov 20, 2019
d426da6
Generate Java validation code for some fields
dmdashenkov Nov 20, 2019
df3ff36
Annotate generated code
dmdashenkov Nov 20, 2019
2980ca7
Add validation for (pattern)
dmdashenkov Nov 20, 2019
a786eae
Remove "Kotlin" from naming
dmdashenkov Nov 21, 2019
e796d58
Fix (pattern) condition
dmdashenkov Nov 21, 2019
d376d7b
Add `NestedClassName`
dmdashenkov Nov 21, 2019
41d8da1
Add a validator factory for numbers
dmdashenkov Nov 21, 2019
2f41bd8
Add a missing space
dmdashenkov Nov 22, 2019
b70d147
Compile an expression to code as is
dmdashenkov Nov 22, 2019
1b0bdfa
Generate number field validation
dmdashenkov Nov 22, 2019
39be2c8
Add validation for message, enum, and byte string fields
dmdashenkov Nov 22, 2019
c9c4500
Fix doc of `(required)`
dmdashenkov Nov 22, 2019
8b6728c
Make validation consistent with the doc
dmdashenkov Nov 22, 2019
1736c6d
Validate collections
dmdashenkov Nov 22, 2019
69f3730
Remove Kotlin from `base-validating-builders`
dmdashenkov Nov 25, 2019
4c3dbab
Remove Kotlin from `validation-generator`
dmdashenkov Nov 25, 2019
51d4231
Remove a JetBrains annotation
dmdashenkov Nov 25, 2019
950f41c
Update reports
dmdashenkov Nov 25, 2019
d660915
Make generated messages implement `ValidatableMessage`
dmdashenkov Nov 25, 2019
023beb8
Make `Validator`s private and nested in the message class and resolve…
dmdashenkov Nov 25, 2019
318eb4e
Fix a warning
dmdashenkov Nov 25, 2019
3222db2
Make the nested class static
dmdashenkov Nov 25, 2019
ca38b46
Remove unused code
dmdashenkov Nov 25, 2019
fc0dbb2
Add a test
dmdashenkov Nov 25, 2019
c0a8ce0
Submit field value with the violation for number fields
dmdashenkov Nov 25, 2019
dae85ea
Add tests for number validation generator
dmdashenkov Nov 25, 2019
a6a5c9b
Parametrize `Expression`
dmdashenkov Nov 25, 2019
0d2d71a
Fix code construction
dmdashenkov Nov 25, 2019
41e0854
Add validation for `Uri`
dmdashenkov Nov 26, 2019
c6d7447
Reformat code
dmdashenkov Nov 26, 2019
9f6c881
Clean up naming and add some doc
dmdashenkov Nov 26, 2019
83f9633
Yet more doc
dmdashenkov Nov 26, 2019
b8ea801
Remove a duplicate `NestedClassName`
dmdashenkov Nov 26, 2019
896beb1
Rename `ValidatableMessage` to `MessageWithConstraints`, clean up API…
dmdashenkov Nov 26, 2019
5417d27
More doc
dmdashenkov Nov 26, 2019
34b89c9
More doc
dmdashenkov Nov 26, 2019
75bf876
Yet more doc
dmdashenkov Nov 26, 2019
81a16ff
Add bounds consistency check
dmdashenkov Nov 26, 2019
5b9434e
Add tests for `NumberBoundaries`
dmdashenkov Nov 26, 2019
03fd72a
Add tests for `MessageValidatorFactory`
dmdashenkov Nov 26, 2019
f0e859e
Add tests for `FieldValidatorFactories`
dmdashenkov Nov 26, 2019
da73a2e
Add yet more doc
dmdashenkov Nov 26, 2019
9e8407c
Remove extra validation
dmdashenkov Nov 26, 2019
9bb1649
Add a test
dmdashenkov Nov 26, 2019
43268c6
Fix a default check
dmdashenkov Nov 26, 2019
f6370f5
Update version
dmdashenkov Nov 26, 2019
2ed8d23
Merge branch 'master' into generate-java-validation
dmdashenkov Nov 26, 2019
1387f8a
Fix validation code
dmdashenkov Nov 27, 2019
297ca8f
Mark Beta API
dmdashenkov Nov 27, 2019
016fcad
Load types into `KnownTypes` for code generation
dmdashenkov Nov 27, 2019
d28dd72
Update reports
dmdashenkov Nov 27, 2019
9051c8d
Add a module for testing generated validation code
dmdashenkov Nov 27, 2019
aca2d3a
Add tests for singular required fields
dmdashenkov Nov 27, 2019
bc9c4ec
Fix collection (required) check
dmdashenkov Nov 28, 2019
6c4127e
Add some more tests for `(required)`
dmdashenkov Nov 28, 2019
32f5dc2
Fix a typo
dmdashenkov Nov 28, 2019
126e2c0
Wrap violations of a message field constraints into a violation of th…
dmdashenkov Nov 28, 2019
6da32dd
Run recursive validation only if the message is not default
dmdashenkov Nov 28, 2019
edb365d
Fix error messages
dmdashenkov Nov 28, 2019
8b36216
Add one more test for (validate)
dmdashenkov Nov 28, 2019
32cb3a0
Fix a test
dmdashenkov Nov 28, 2019
c5e744d
More tests for numerical constraints
dmdashenkov Nov 28, 2019
2a55364
Fix a typo
dmdashenkov Nov 28, 2019
674a6d2
Add one more (compilation-level) test
dmdashenkov Nov 28, 2019
4bc405d
Document `RecursiveValidation`
dmdashenkov Nov 28, 2019
0b230da
Fix a typo
dmdashenkov Nov 28, 2019
32633ff
Make tests for code gen work with non-empty types
dmdashenkov Nov 28, 2019
4375dd5
Add tests for generated code for collection validation
dmdashenkov Nov 28, 2019
42837e3
Add tests for new API of `Messages`
dmdashenkov Nov 28, 2019
d3ecf8b
Define `ViolationAccumulator`
dmdashenkov Nov 29, 2019
7598506
Extract common part when constructing `Boundary`
dmdashenkov Nov 29, 2019
5bb4f2e
Better alignment
dmdashenkov Nov 29, 2019
f97f055
Update config
dmdashenkov Nov 29, 2019
1a91616
Change type of thrown exception
dmdashenkov Dec 2, 2019
84bd98b
Document `toString` implementation
dmdashenkov Dec 2, 2019
6fa92eb
Rename `Validate.violations` to `Validate.violationsOf` and return `I…
dmdashenkov Dec 2, 2019
dd060d1
Rename `SpineProtoGenerator` to `CodeGenerator` and update doc
dmdashenkov Dec 2, 2019
3058d56
Add some more doc for `Expression`
dmdashenkov Dec 2, 2019
9e9f251
Add aliases for function types
dmdashenkov Dec 2, 2019
e1c57d3
Remove unnecessary check
dmdashenkov Dec 2, 2019
15a3d64
Move `NoOpFactory` to the upper level
dmdashenkov Dec 2, 2019
8aaa4c6
Move `NumberKind` to the upper level
dmdashenkov Dec 2, 2019
c73ff36
Fix a typo
dmdashenkov Dec 2, 2019
2729aef
Rename `RecursiveValidation` into `NestedConstraints`
dmdashenkov Dec 2, 2019
9c1bb7b
Improve doc
dmdashenkov Dec 2, 2019
2b10e56
Rename `IsEmpty` to `ContainerFields`
dmdashenkov Dec 2, 2019
b71abbe
Rename `CompilerOutput.from(..)` to `CompilerOutput.wrapping(..)`
dmdashenkov Dec 2, 2019
223953f
More doc for `Rule` and `NestedConstraints`
dmdashenkov Dec 2, 2019
9e9efd7
Increase type safety of `Expression`s and remove functions where expr…
dmdashenkov Dec 4, 2019
0fbe9b6
Change naming in `Constraint` hierarchy
dmdashenkov Dec 4, 2019
2108ecc
Improve doc in `MessageWithConstraints`
dmdashenkov Dec 4, 2019
9e7eb5b
Rename `ViolationTemplate` to `NewViolation`
dmdashenkov Dec 4, 2019
82dde7d
Rename `ValidatorGenerator` to `ValidatorCode`
dmdashenkov Dec 4, 2019
ea92f1a
Fix test display names
dmdashenkov Dec 4, 2019
c07d287
Fix code alignment
dmdashenkov Dec 4, 2019
e96d5af
Update reports
dmdashenkov Dec 4, 2019
f60eba8
Rephrase doc for `Constraint`
dmdashenkov Dec 5, 2019
69caf3e
Rephrase doc for `MessageConstraints`
dmdashenkov Dec 5, 2019
5a72b21
Clean up `BooleanExpression` API and add doc
dmdashenkov Dec 5, 2019
f37f383
Fix a typo
dmdashenkov Dec 5, 2019
cbd910e
Rephrase doc
dmdashenkov Dec 5, 2019
6a71905
Remove tautology in doc
dmdashenkov Dec 5, 2019
f53c614
Split validation constraints and validation execution in runtime
dmdashenkov Dec 9, 2019
b1a7b94
Remove tautology in doc
dmdashenkov Dec 9, 2019
655e4bd
Remove redundant type params
dmdashenkov Dec 9, 2019
397c744
Delete a duplicate interface and assemble all field constraints
dmdashenkov Dec 10, 2019
58c7214
Fix an error
dmdashenkov Dec 10, 2019
dcb2e9d
Add backticks for an error message
dmdashenkov Dec 10, 2019
b98e346
Fix some tests
dmdashenkov Dec 11, 2019
ae22ccd
Disable logging tests
dmdashenkov Dec 11, 2019
319cf9c
Delete some tests
dmdashenkov Dec 11, 2019
0201fdd
Add `(goes).with` option to fields which can be `(required)`
dmdashenkov Dec 11, 2019
0770f64
Remove redundant annotation
dmdashenkov Dec 11, 2019
54eaf8a
Fix a warning
dmdashenkov Dec 11, 2019
c343ef5
Implement required_field check and clean up
dmdashenkov Dec 11, 2019
4c0ea69
Re-write (required_field) check and delete unused code
dmdashenkov Dec 12, 2019
909f2be
Fix errors
dmdashenkov Dec 12, 2019
f0a4f96
Pass field context to validated message field
dmdashenkov Dec 12, 2019
f8d7419
Update config
dmdashenkov Dec 12, 2019
b656248
Fix tests
dmdashenkov Dec 12, 2019
d63dff0
Clean up
dmdashenkov Dec 12, 2019
3ffa8ab
Use a stream instead of a list
dmdashenkov Dec 12, 2019
4f9e2da
Implement some checks in `ConstraintCompiler`
dmdashenkov Dec 13, 2019
2045957
Fix error
dmdashenkov Dec 16, 2019
c81ed67
Add line breaks
dmdashenkov Dec 16, 2019
beae1ce
Compile constraints linearly
dmdashenkov Dec 16, 2019
83cf70c
Fix an error
dmdashenkov Dec 16, 2019
23032e4
Remove `GetterExpression` and make `MessageAccess` and `FieldAccess` …
dmdashenkov Dec 16, 2019
d008f50
Remove an unused import
dmdashenkov Dec 16, 2019
c77a779
Validate each element if repeated of map
dmdashenkov Dec 16, 2019
fcd511a
Fix formatting and update an assertion
dmdashenkov Dec 16, 2019
ea92f49
Consolidate checks in `ConstraintCode` and add support for missing co…
dmdashenkov Dec 16, 2019
487b7cf
Fix constraint assembly
dmdashenkov Dec 17, 2019
4daea39
Fix a compilation error in tests
dmdashenkov Dec 17, 2019
3c60872
Fix a typo
dmdashenkov Dec 17, 2019
fdfddf7
Make generated violations cleaner
dmdashenkov Dec 17, 2019
3296bb3
Fix the `required` check for collections
dmdashenkov Dec 17, 2019
c31c33d
Run validation only if the field value is non-default
dmdashenkov Dec 17, 2019
b2ddfaa
Remove an unnecessary check
dmdashenkov Dec 17, 2019
04a0e61
Add tests for generated (required_field) checks
dmdashenkov Dec 18, 2019
2537a4e
Add tests for generated (goes) checks
dmdashenkov Dec 18, 2019
b02ff4a
Update a report
dmdashenkov Dec 18, 2019
189360d
Merge branch 'master' into generate-java-validation
dmdashenkov Dec 18, 2019
12dbd05
Add some missing doc
dmdashenkov Dec 18, 2019
d6e538e
Add tests for required IDs
dmdashenkov Dec 18, 2019
cc2bf27
One more test for `required`
dmdashenkov Dec 18, 2019
2e02b3f
More doc
dmdashenkov Dec 18, 2019
06f11b2
More doc
dmdashenkov Dec 18, 2019
1622147
Remove redundant `Internal` annotation
dmdashenkov Dec 18, 2019
ef85165
Add tests for `(distinct)`
dmdashenkov Dec 18, 2019
9a29760
Add missing FQN and a line break in generated code
dmdashenkov Dec 18, 2019
6ccf49b
Fix a line break
dmdashenkov Dec 18, 2019
11faf75
Remove an unused method
dmdashenkov Dec 18, 2019
08ffcb5
Suppress a warning
dmdashenkov Dec 18, 2019
17731fd
Add doc for `ConstraintCompiler`
dmdashenkov Dec 18, 2019
6950db4
Extract code from `ConstraintCompiler`
dmdashenkov Dec 18, 2019
df31829
Remove empty line
dmdashenkov Dec 19, 2019
c3860a9
Add the `CustomConstraint` for extension purposes
dmdashenkov Dec 19, 2019
4461c34
Add tests for `ConstraintCompiler`
dmdashenkov Dec 19, 2019
d2deeef
Call custom validation from generated code
dmdashenkov Dec 19, 2019
14f6f01
Add tests for custom constraints in in generated code
dmdashenkov Dec 19, 2019
f9d10c0
Document new classes
dmdashenkov Dec 19, 2019
b54b30f
Remove a redundant interface
dmdashenkov Dec 20, 2019
a74996b
Improve doc
dmdashenkov Dec 20, 2019
522e41f
More doc
dmdashenkov Dec 20, 2019
60f52bd
Better and doc for standard option factories
dmdashenkov Dec 20, 2019
91b2cd0
More doc
dmdashenkov Dec 20, 2019
e56bb98
Remove dead code
dmdashenkov Dec 20, 2019
4323589
Yet more doc
dmdashenkov Dec 20, 2019
0a00d60
Add missing violation params and improve doc
dmdashenkov Dec 20, 2019
3be1b5e
Make `goes` only work with what can be `required`
dmdashenkov Dec 20, 2019
d22ea48
Use `implementation`/`api` configurations instead of `compile` for Pr…
dmdashenkov Dec 20, 2019
5ae4cb0
Update Gradle in smoke tests
dmdashenkov Dec 20, 2019
a4dae89
Resolve infinite recursion
dmdashenkov Dec 20, 2019
ef4c038
Cache custom field constraints
dmdashenkov Dec 23, 2019
90799eb
Make generated validation check Any by unpacking
dmdashenkov Dec 23, 2019
68a9c52
Cache collected `Constraints`
dmdashenkov Dec 23, 2019
c0942a2
Catch and wrap cache loading errors
dmdashenkov Dec 24, 2019
179bd8c
Add doc
dmdashenkov Dec 24, 2019
91670ad
Update smoke tests
dmdashenkov Dec 24, 2019
c9f1de1
Add some more doc
dmdashenkov Dec 24, 2019
1af8417
Add a test for `Validate.violationsOfCustomConstraints(..)`
dmdashenkov Dec 24, 2019
30332fe
Rename `ConstraintCompiler` to `ValidationCodeGenerator` and `Constra…
dmdashenkov Dec 24, 2019
83a8604
Add doc for methods of `ConstraintTranslator`
dmdashenkov Dec 24, 2019
7f55fc9
Fix a typo
dmdashenkov Dec 24, 2019
6c1e540
Fix a typo
dmdashenkov Dec 24, 2019
15aacda
Improve doc and move some code-related terms to `.code` package
dmdashenkov Dec 24, 2019
7f23d77
Format code and delete unused API
dmdashenkov Dec 26, 2019
497e05b
Fix grammar
dmdashenkov Dec 26, 2019
797c888
Remove a duplicate API
dmdashenkov Dec 26, 2019
505c489
Improve doc
dmdashenkov Dec 26, 2019
1e8229f
Extract caching from `Constraints`.
dmdashenkov Dec 26, 2019
a332e6e
And tests and doc for `Duplicates`
dmdashenkov Dec 26, 2019
a9a2e92
Rename `NumberConversionChecker` to `NumberConversion` and remove dup…
dmdashenkov Dec 26, 2019
40954f9
Fix data source method names
dmdashenkov Dec 26, 2019
86a40ee
Fix grammar
dmdashenkov Dec 26, 2019
7ef26df
Fix grammar
dmdashenkov Dec 26, 2019
f83ef5b
Fix grammar
dmdashenkov Dec 26, 2019
aab2661
Improve doc and formatting
dmdashenkov Dec 26, 2019
51a9bc9
Make an error message helpful
dmdashenkov Dec 26, 2019
7a7040c
Helpful violation message
dmdashenkov Dec 26, 2019
b8be9b4
Fix grammar in violation messages
dmdashenkov Dec 26, 2019
8101065
Fix a doc
dmdashenkov Dec 26, 2019
3f9a21d
Add docs for test types
dmdashenkov Dec 26, 2019
569bad5
Add an example for `AccumulateViolations`
dmdashenkov Dec 26, 2019
a0cdd2e
Improve doc
dmdashenkov Dec 26, 2019
3b68483
Better alignment
dmdashenkov Dec 26, 2019
24bca04
Add doc
dmdashenkov Dec 26, 2019
d33c0f7
Bump version to 1.4.0
dmdashenkov Dec 26, 2019
33569b3
Rename `MessageValidatorFactory` to `ValidateGenerator`
dmdashenkov Dec 26, 2019
9cad0e6
Fix an error in smoke tests
dmdashenkov Dec 26, 2019
db99ccc
Remove warning suppressions
dmdashenkov Dec 26, 2019
7318dd1
More doc
dmdashenkov Dec 26, 2019
79c1130
Fix articles in error message
alexander-yevsyukov Dec 26, 2019
aad65c3
Fix language in the range error message
alexander-yevsyukov Dec 26, 2019
946242e
Improve layout
alexander-yevsyukov Dec 26, 2019
f42ff77
Remove "between"
dmdashenkov Dec 27, 2019
94f77ac
Merge branch 'generate-java-validation' of github.com:SpineEventEngin…
dmdashenkov Dec 27, 2019
e19a583
Change version to 1.3.1
dmdashenkov Dec 30, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions base-validating-builders/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ apply plugin: 'com.google.protobuf'
apply plugin: 'io.spine.tools.spine-model-compiler'
apply plugin: 'idea'

modelCompiler {
generateValidation = true
}

repositories {
// This defines the `libs` directory of upstream projects as a local repository.
// See `dependencies` section below for definition of the dependency on the JAR produced by
Expand Down
13 changes: 9 additions & 4 deletions base/src/main/java/io/spine/code/java/ClassName.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@

import java.util.Deque;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Lists.newLinkedList;
import static io.spine.code.java.SimpleClassName.OR_BUILDER_SUFFIX;
import static io.spine.util.Preconditions2.checkNotEmptyOrBlank;
Expand Down Expand Up @@ -313,11 +313,16 @@ private static String afterDot(String fullName) {
* Obtains the name of the package of this class.
*/
public PackageName packageName() {
int packageEndIndex = packageEndIndex();
String result = value().substring(0, packageEndIndex);
return PackageName.of(result);
}

private int packageEndIndex() {
String fullName = value();
int lastDotIndex = fullName.lastIndexOf(DOT_SEPARATOR);
checkArgument(lastDotIndex > 0, "%s should be qualified.", fullName);
String result = fullName.substring(0, lastDotIndex);
return PackageName.of(result);
checkState(lastDotIndex > 0, "%s should be qualified.", fullName);
return lastDotIndex;
}

/**
Expand Down
31 changes: 28 additions & 3 deletions base/src/main/java/io/spine/code/proto/FieldContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,17 @@ public FieldContext forChild(FieldDescriptor child) {
return new FieldContext(this, child);
}

/**
* Obtains {@code FieldContext} for the specified child.
*
* @param child
* the child declaration
* @return the child declaration context
*/
public FieldContext forChild(FieldDeclaration child) {
return forChild(child.descriptor());
}

/**
* Obtains target of this context.
*
Expand All @@ -105,6 +116,16 @@ public FieldDescriptor target() {
return checkNotNull(target, "Empty context cannot have a target.");
}

/**
* Obtains target of this context as a {@link FieldDeclaration}.
*
* @return the target declaration
*/
public FieldDeclaration targetDeclaration() {
FieldDescriptor target = target();
return new FieldDeclaration(target);
}

private Optional<FieldDescriptor> targetParent() {
return parent == null ? Optional.empty() : Optional.ofNullable(parent.target);
}
Expand Down Expand Up @@ -164,12 +185,16 @@ public boolean equals(Object o) {
return false;
}
FieldContext context = (FieldContext) o;
return Objects.equals(target, context.target) &&
Objects.equals(parent, context.parent);
return Objects.equals(targetNameOrEmpty(), context.targetNameOrEmpty())
&& Objects.equals(parent, context.parent);
}

@Override
public int hashCode() {
return Objects.hash(target, parent);
return Objects.hash(targetNameOrEmpty(), parent);
}

private String targetNameOrEmpty() {
return target != null ? target.getFullName() : "";
}
}
75 changes: 57 additions & 18 deletions base/src/main/java/io/spine/code/proto/FieldDeclaration.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

import com.google.common.base.Objects;
import com.google.errorprone.annotations.Immutable;
import com.google.protobuf.Any;
import com.google.protobuf.DescriptorProtos.FieldDescriptorProto;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.Descriptors.FieldDescriptor.JavaType;
Expand Down Expand Up @@ -128,21 +129,35 @@ private boolean sameMessageType(Message msg) {
}

/**
* Obtains fully-qualified name of the Java class that corresponds to the declared type
* of the field.
* Obtains fully-qualified canonical name of the Java class that corresponds to the declared
* type of the field.
*
* <p>If the field is {@code repeated}, obtains the name of the elements.
*
* <p>If the field is a {@code map}, obtains the name of the values.
*/
public String javaTypeName() {
return isMap()
? javaTypeName(valueDeclaration().field)
: javaTypeName(this.field);
}

private static String javaTypeName(FieldDescriptor field) {
FieldDescriptor.Type fieldType = field.getType();
if (fieldType == MESSAGE) {
return messageClassName();
MessageType messageType =
new MessageType(field.getMessageType());
return messageType.javaClassName()
.canonicalName();
}

if (fieldType == ENUM) {
return enumClassName();
EnumType enumType = EnumType.create(field.getEnumType());
return enumType.javaClassName()
.canonicalName();
}

return ScalarType.javaTypeName(field.toProto()
.getType());
return ScalarType.javaTypeName(field.toProto().getType());
}

private String messageClassName() {
Expand All @@ -161,11 +176,6 @@ private String messageClassName() {
}
}

private String enumClassName() {
EnumType enumType = EnumType.create(field.getEnumType());
return enumType.javaClassName().value();
}

/**
* Determines whether the field is an entity ID.
*
Expand All @@ -177,21 +187,28 @@ private String enumClassName() {
* </ul>
*
* @return {@code true} if the field is an entity ID, {@code false} otherwise
* @see #isId()
*/
public boolean isEntityId() {
return isFirstField() && isEntityField() && isNotCollection();
}

/**
* Determines whether the field is a command ID.
* Determines whether the field is an ID.
*
* <p>A command ID is the first field of a message declared in a
* {@link MessageFile#COMMANDS commands file}.
* <p>An ID satisfies the following conditions:
* <ul>
* <li>Declared as the first field.
* <li>Declared inside an {@linkplain EntityOption#getKind() entity state message} or
* a {@linkplain io.spine.base.CommandMessage command message};
* <li>Is not a map or a repeated field.
* </ul>
*
* @return {@code true} if the field is a command ID, {@code false} otherwise
* @return {@code true} if the field is an entity ID, {@code false} otherwise
*/
public boolean isCommandId() {
return isFirstField() && isCommandsFile();
public boolean isId() {
boolean fieldMatches = isFirstField() && isNotCollection();
return fieldMatches && (isCommandsFile() || isEntityField());
}

/**
Expand All @@ -209,16 +226,28 @@ public boolean isString() {
}

/**
* Tells if the field is of enum type.
* Tells if the field is of an enum type.
*/
public boolean isEnum() {
return field.getType() == ENUM;
}

/**
* Tells if the field is of a message type.
*/
public boolean isMessage() {
return field.getType() == MESSAGE;
}

/**
* Tells if the field is of type {@code google.protobuf.Any}.
*/
public boolean isAny() {
return isMessage() && field.getMessageType()
.getFullName()
.equals(Any.getDescriptor().getFullName());
}

/**
* Determines whether the declaration is a singular value.
*
Expand Down Expand Up @@ -356,4 +385,14 @@ public boolean equals(Object o) {
public int hashCode() {
return Objects.hashCode(declaringMessage, field.getFullName());
}

/**
* Obtains qualified name of this field.
*
* <p>Example: {@code spine.net.Uri.protocol}.
*/
@Override
alexander-yevsyukov marked this conversation as resolved.
Show resolved Hide resolved
public String toString() {
return field.getFullName();
}
}
13 changes: 12 additions & 1 deletion base/src/main/java/io/spine/code/proto/Option.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,19 @@ public interface Option<@ImmutableTypeParameter T,
* Obtains the value of this option for the specified object that holds it.
*
* @param object
* holder of the option
* the option holder
* @return value of this option
alexander-yevsyukov marked this conversation as resolved.
Show resolved Hide resolved
*/
Optional<T> valueFrom(K object);

/**
* Checks if the option is declared on the given holder.
*
* @param object
* the option holder
* @return {@code true} if the option is declared and is non-default, {@code false} otherwise
*/
default boolean valuePresent(K object) {
return valueFrom(object).isPresent();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ private OptionExtensionRegistry() {
}

/**
* Obtains the {@link ExtensionRegistry} with all the {@code
* spine/options.proto} extensions.
* Obtains the {@link ExtensionRegistry} with all the {@code spine/options.proto} extensions.
*/
public static ExtensionRegistry instance() {
return EXTENSIONS;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,33 +18,27 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package io.spine.validate.option;
package io.spine.protobuf;

import com.google.common.collect.ImmutableList;
import com.google.errorprone.annotations.Immutable;
import com.google.errorprone.annotations.ImmutableTypeParameter;
import com.google.protobuf.Message;
import io.spine.annotation.Beta;
import io.spine.validate.ConstraintViolation;

/**
* A rule that limits the set of possible values for {@code T} and produces
* {@code ConstraintViolations} upon finding values of {@code T} which do not fit into the
* set limits.
* A message with validation constraints.
*
* @param <T>
* a type of values that this constraint is applicable to
* <p>Please see {@code spine/options.proto} for the definitions of validation options.
*/
@Beta
@Immutable
public interface Constraint<@ImmutableTypeParameter T> {
public interface MessageWithConstraints extends Message {

/**
* Checks the specified value against this constraint.
* Validates this message according to the rules in the Protobuf definition.
*
* <p>If the value has breaks the rules imposed by this constraint, {@code ConstraintViolations}
* are produced and returned.
*
* @param value
* value that is being checked against this constraint
* @return violations of this constraint
* @return a list of {@code ConstraintViolation}s or an empty list if the message is valid
*/
ImmutableList<ConstraintViolation> check(T value);
ImmutableList<ConstraintViolation> validate();
}
25 changes: 25 additions & 0 deletions base/src/main/java/io/spine/protobuf/Messages.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import com.google.protobuf.Any;
import com.google.protobuf.Message;
import com.google.protobuf.MessageLite;
import com.google.protobuf.ProtocolMessageEnum;
import io.spine.annotation.Internal;

import java.lang.reflect.InvocationTargetException;
Expand Down Expand Up @@ -137,6 +138,30 @@ public static boolean isNotDefault(Message object) {
return result;
}

/**
* Verifies if the passed Protobuf enum element is the enum's default state.
*
* @param messageEnum
* the enum element to inspect
* @return {@code true} if the passed enum is default, {@code false} otherwise
*/
public static boolean isDefault(ProtocolMessageEnum messageEnum) {
checkNotNull(messageEnum);
return messageEnum.getNumber() == 0;
}

/**
* Verifies if the passed Protobuf enum element is NOT the enum's default state.
*
* @param messageEnum
* the enum element to inspect
* @return {@code false} if the passed enum is default, {@code true} otherwise
*/
public static boolean isNotDefault(ProtocolMessageEnum messageEnum) {
checkNotNull(messageEnum);
return !isDefault(messageEnum);
}

/**
* The loader of the cache of default instances per {@link Message} class.
*
Expand Down
7 changes: 4 additions & 3 deletions base/src/main/java/io/spine/protobuf/TypeConverter.java
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,6 @@ protected Message toMessage(Message input) {
* best-performant solution among options, such as {@link Class#getCanonicalName()
* Class.getCanonicalName()}.
*/
@SuppressWarnings("OverlyCoupledClass") // OK, as references a lot of type for casting.
private static final class PrimitiveTypeCaster<M extends Message, T>
extends MessageCaster<M, T> {

Expand Down Expand Up @@ -325,7 +324,8 @@ private static final class PrimitiveTypeCaster<M extends Message, T>
@Override
protected T toObject(M input) {
Class<?> boxedType = input.getClass();
@SuppressWarnings("unchecked") Converter<M, T> typeUnpacker =
@SuppressWarnings("unchecked")
Converter<M, T> typeUnpacker =
(Converter<M, T>) PROTO_WRAPPER_TO_HANDLER.get(boxedType);
checkArgument(typeUnpacker != null,
"Could not find a primitive type for %s.",
Expand All @@ -337,7 +337,8 @@ protected T toObject(M input) {
@Override
protected M toMessage(T input) {
Class<?> cls = input.getClass();
@SuppressWarnings("unchecked") Converter<M, T> converter =
@SuppressWarnings("unchecked")
Converter<M, T> converter =
(Converter<M, T>) PRIMITIVE_TO_HANDLER.get(cls);
checkArgument(converter != null,
"Could not find a wrapper type for %s.",
Expand Down
8 changes: 4 additions & 4 deletions base/src/main/java/io/spine/type/KnownTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -193,14 +193,14 @@ public boolean contains(TypeUrl typeUrl) {
return type;
}

private Type get(TypeName name) throws UnknownTypeException {
Type result = typeSet.find(name)
.orElseThrow(() -> new UnknownTypeException(name.value()));
private Type<?, ?> get(TypeName name) throws UnknownTypeException {
Type<?, ?> result = typeSet.find(name)
.orElseThrow(() -> new UnknownTypeException(name.value()));
return result;
}

private ClassName get(TypeUrl typeUrl) {
Type type = get(typeUrl.toTypeName());
Type<?, ?> type = get(typeUrl.toTypeName());
ClassName result = type.javaClassName();
return result;
}
Expand Down
Loading