Skip to content

Commit

Permalink
protostuff.runtime.preserve_null_elements replaces protostuff.runtime…
Browse files Browse the repository at this point in the history
….allow_null_array_element, which now applies to both collections and arrays
  • Loading branch information
dyu committed May 24, 2020
1 parent 6f23190 commit e2dec77
Show file tree
Hide file tree
Showing 14 changed files with 360 additions and 58 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
protostuff-1.8.x
- `-Dprotostuff.runtime.preserve_null_elements=true` preserves null elements in collections/arrays.
This property replaces `-Dprotostuff.runtime.allow_null_array_element=true`, which only applied to arrays.
Note that this works only on the binary formats (protostuff/protobuf/graph)

protostuff-1.7.2 2020-04-18
===========================
- Issue 273 - fix bug on custom maps without a K (Key) parameter type. Only the V (value) parameter type is declared.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,8 @@
/**
* A schema for standard jdk {@link Collection collections}. Null values are not serialized/written.
* <p>
* If your application relies on {@link Object#equals(Object)}, it will fail when a serialized collection contains null
* values (The deserialized collection will not contained the null value). {@link MapSchema} on the otherhand can
* contain both null keys and null values and still succeeding on {@link Object#equals(Object)}.
* If {@link #preserveNull} is false and your application relies on {@link Object#equals(Object)},
* it will fail when a serialized collection contains null values.
*
* @author David Yu
* @created Jan 26, 2011
Expand All @@ -34,6 +33,7 @@ public abstract class CollectionSchema<V> implements Schema<Collection<V>>
{

public static final String FIELD_NAME_VALUE = "v";
public static final String FIELD_NAME_NULL = "n";

/**
* Creates new {@code Collection} messages.
Expand Down Expand Up @@ -340,15 +340,18 @@ public static boolean accept(String name)
* Factory for creating {@link Collection} messages.
*/
public final MessageFactory messageFactory;

public final boolean preserveNull;

public CollectionSchema()
public CollectionSchema(boolean preserveNull)
{
this(MessageFactories.ArrayList);
this(MessageFactories.ArrayList, preserveNull);
}

public CollectionSchema(MessageFactory messageFactory)
public CollectionSchema(MessageFactory messageFactory, boolean preserveNull)
{
this.messageFactory = messageFactory;
this.preserveNull = preserveNull;
}

/**
Expand All @@ -372,13 +375,26 @@ protected abstract void transferValue(Pipe pipe, Input input, Output output,
@Override
public final String getFieldName(int number)
{
return number == 1 ? FIELD_NAME_VALUE : null;
switch (number)
{
case 1: return FIELD_NAME_VALUE;
case 2: return FIELD_NAME_NULL;
default: return null;
}
}

@Override
public final int getFieldNumber(String name)
{
return name.length() == 1 && name.charAt(0) == 'v' ? 1 : 0;
if (1 != name.length())
return 0;

switch (name.charAt(0))
{
case 'v': return 1;
case 'n': return 2;
default: return 0;
}
}

@Override
Expand Down Expand Up @@ -414,7 +430,7 @@ public final Collection<V> newMessage()
@Override
public void mergeFrom(Input input, Collection<V> message) throws IOException
{
for (int number = input.readFieldNumber(this);; number = input.readFieldNumber(this))
for (int number = input.readFieldNumber(this), i, nullCount;; number = input.readFieldNumber(this))
{
switch (number)
{
Expand All @@ -423,24 +439,57 @@ public void mergeFrom(Input input, Collection<V> message) throws IOException
case 1:
addValueFrom(input, message);
break;
case 2:
for (nullCount = input.readUInt32(), i = 0; i < nullCount; i++)
message.add(null);
break;
default:
throw new ProtostuffException("The collection was incorrectly " +
"serialized.");
throw new ProtostuffException("The collection was incorrectly serialized.");
}
}
}

@Override
public void writeTo(Output output, Collection<V> message) throws IOException

private void writeWithNullTo(Output output, Collection<V> message) throws IOException
{
int nullCount = 0;
for (V value : message)
{
if (value != null)
{
if (nullCount != 0)
{
output.writeUInt32(2, nullCount, false);
nullCount = 0;
}
writeValueTo(output, 1, value, true);
}
else
{
nullCount++;
}
}
if (nullCount != 0)
output.writeUInt32(2, nullCount, false);
}

private void writeWithoutNullTo(Output output, Collection<V> message) throws IOException
{
for (V value : message)
{
// null values not serialized.
if (value != null)
writeValueTo(output, 1, value, true);
}
}

@Override
public void writeTo(Output output, Collection<V> message) throws IOException
{
if (preserveNull)
writeWithNullTo(output, message);
else
writeWithoutNullTo(output, message);
}

public final Pipe.Schema<Collection<V>> pipeSchema =
new Pipe.Schema<Collection<V>>(CollectionSchema.this)
{
Expand All @@ -458,9 +507,11 @@ protected void transfer(Pipe pipe, Input input, Output output) throws IOExceptio
case 1:
transferValue(pipe, input, output, 1, true);
break;
case 2:
output.writeUInt32(2, input.readUInt32(), false);
break;
default:
throw new ProtostuffException("The collection was incorrectly " +
"serialized.");
throw new ProtostuffException("The collection was incorrectly serialized.");
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,14 @@ public final class MessageCollectionSchema<V> extends CollectionSchema<V>
*/
public final Pipe.Schema<V> pipeSchema;

public MessageCollectionSchema(Schema<V> schema)
public MessageCollectionSchema(Schema<V> schema, boolean preserveNull)
{
this(schema, null);
this(schema, null, preserveNull);
}

public MessageCollectionSchema(Schema<V> schema, Pipe.Schema<V> pipeSchema)
public MessageCollectionSchema(Schema<V> schema, Pipe.Schema<V> pipeSchema, boolean preserveNull)
{
super(preserveNull);
this.schema = schema;
this.pipeSchema = pipeSchema;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public final class RuntimeEnv
* Disabled by default. Writes a sentinel value (uint32) in place of null values. Works only on the binary formats
* (protostuff/graph/protobuf).
*/
public static final boolean ALLOW_NULL_ARRAY_ELEMENT;
public static final boolean PRESERVE_NULL_ELEMENTS;

/**
* Disabled by default. For pojos that are not declared final, they could still be morphed to their respective
Expand Down Expand Up @@ -187,8 +187,8 @@ public final class RuntimeEnv
AUTO_LOAD_POLYMORPHIC_CLASSES = Boolean.parseBoolean(props.getProperty(
"protostuff.runtime.auto_load_polymorphic_classes", "true"));

ALLOW_NULL_ARRAY_ELEMENT = Boolean.parseBoolean(props.getProperty(
"protostuff.runtime.allow_null_array_element", "false"));
PRESERVE_NULL_ELEMENTS = Boolean.parseBoolean(props.getProperty(
"protostuff.runtime.preserve_null_elements", "false"));

MORPH_NON_FINAL_POJOS = Boolean.parseBoolean(props.getProperty(
"protostuff.runtime.morph_non_final_pojos", "false"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ public Base(IdStrategy strategy, final Handler handler)

this.handler = handler;

allowNullArrayElement = 0 != (IdStrategy.ALLOW_NULL_ARRAY_ELEMENT & strategy.flags);
allowNullArrayElement = 0 != (IdStrategy.PRESERVE_NULL_ELEMENTS & strategy.flags);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public abstract class IdStrategy
public static final int
ENUMS_BY_NAME = 1,
AUTO_LOAD_POLYMORPHIC_CLASSES = 1 << 1,
ALLOW_NULL_ARRAY_ELEMENT = 1 << 2,
PRESERVE_NULL_ELEMENTS = 1 << 2,
MORPH_NON_FINAL_POJOS = 1 << 3,
MORPH_COLLECTION_INTERFACES = 1 << 4,
MORPH_MAP_INTERFACES = 1 << 5,
Expand All @@ -56,8 +56,8 @@ public abstract class IdStrategy
if (RuntimeEnv.AUTO_LOAD_POLYMORPHIC_CLASSES)
flags |= AUTO_LOAD_POLYMORPHIC_CLASSES;

if (RuntimeEnv.ALLOW_NULL_ARRAY_ELEMENT)
flags |= ALLOW_NULL_ARRAY_ELEMENT;
if (RuntimeEnv.PRESERVE_NULL_ELEMENTS)
flags |= PRESERVE_NULL_ELEMENTS;

if (RuntimeEnv.MORPH_NON_FINAL_POJOS)
flags |= MORPH_NON_FINAL_POJOS;
Expand Down Expand Up @@ -197,6 +197,9 @@ else if (groupId != 0)
throw new RuntimeException("An IdStrategy without a primaryGroup "
+ "(standalone) must have a groupId of zero.");
}
// implicit
if (0 != (flags & PRESERVE_NULL_ELEMENTS))
flags |= COLLECTION_SCHEMA_ON_REPEATED_FIELDS;

this.flags = flags;
this.primaryGroup = primaryGroup;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
* Will be used if either {@link RuntimeEnv#POJO_SCHEMA_ON_COLLECTION_FIELDS} or
* {@link RuntimeEnv#POJO_SCHEMA_ON_MAP_FIELDS} is set.
*
* Applies to a field which is an interface and the type is assignable from List or Map.
* Applies to a field which is an interface and the type is assignable from Collection or Map.
*
* @author David Yu
* @created May 19, 2016
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ abstract class RuntimeCollectionField<T, V> extends Field<T>
protected final CollectionSchema<V> schema;

public RuntimeCollectionField(FieldType type, int number, String name,
Tag tag, MessageFactory messageFactory)
Tag tag, MessageFactory messageFactory, boolean allowNullElement)
{
super(type, number, name, false, tag);
schema = new CollectionSchema<V>(messageFactory)
schema = new CollectionSchema<V>(messageFactory, allowNullElement)
{
@Override
protected void addValueFrom(Input input, Collection<V> collection)
Expand Down
Loading

0 comments on commit e2dec77

Please sign in to comment.