-
Notifications
You must be signed in to change notification settings - Fork 15.9k
Description
Scenario:
- get field builder on oneof Message field
- set another field on this oneof
- mutate any field on the field builder (1)
Expected:
There are 2 alternatives:
- the exception like "this oneof field builder is unset" is raised, or
- the oneof and its case is set back to this field
Actual:
The field builder allows to mutate it with no warnings, and the oneof case is not changed back to this field!
Proposed Remedy:
Add extra oneofFieldChanged(Internal.EnumLite | FieldDescriptor) method to GeneratedMessage.BuilderParent interface, and notify parent of any oneof field builder's mutations.
When notified, either throw an exception if oneof is set to different case, or change the oneof's case to this field's case.
The good news is that it just clarifies the existing behavior and doesn't change any public interfaces and the existing techniques. Not implementing this fix will cause numerous, difficult to trace, logical problems with oneof in such a common scenario.
In addition, due to the wrong bundle number bug, you have to issue minor update anyway; so this would be the opportune time for the proposed solution.
Cheers,
David
PS: Here is the full JUnit 4 test case for the scenario as-is:
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import org.junit.Test;
import protobuf_unittest.UnittestProto.TestAllTypes;
import protobuf_unittest.UnittestProto.TestAllTypes.Builder;
import protobuf_unittest.UnittestProto.TestAllTypes.OneofFieldCase;
public class OneofPropertyChangeTest {
@test
public void testOneOfPropertyChangeGeneratedBuilder() {
final Builder builder = TestAllTypes.newBuilder().setOneofString("hello");
assertThat(builder.getOneofFieldCase(), is(equalTo(OneofFieldCase.ONEOF_STRING)));
final TestAllTypes.NestedMessage.Builder innerBuilder = builder.getOneofNestedMessageBuilder();
assertThat(builder.getOneofFieldCase(), is(equalTo(OneofFieldCase.ONEOF_NESTED_MESSAGE)));
builder.setOneofString("hello again");
assertThat(builder.getOneofFieldCase(), is(equalTo(OneofFieldCase.ONEOF_STRING)));
/**
* when oneof's field builder property changes, either set oneof to this
* field, or raise exception when other field set on this oneof already!
* At present, the builder will allow any its mutation with no effect on its parent!
*/
innerBuilder.setBb(999);
// the following should not be failing, but it fails!
assertThat(builder.getOneofFieldCase(), is(equalTo(OneofFieldCase.ONEOF_NESTED_MESSAGE)));
}
}