Skip to content

Commit

Permalink
Merge pull request #17 from VenomVendor/bugfix/serialization-fix
Browse files Browse the repository at this point in the history
Bugfix/serialization fix
  • Loading branch information
VenomVendor authored Jul 26, 2018
2 parents 38729a9 + 50e9a0d commit 52c73d4
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 45 deletions.
16 changes: 12 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
# NullDefense | Gson
# [NullDefense | Gson](https://venomvendor.github.io/NullDefense)
> Removes invalid objects during Gson parsing which are marked as required, yet null/empty.
[![Build Status](https://travis-ci.org/VenomVendor/NullDefense.svg?branch=master)](https://travis-ci.org/VenomVendor/NullDefense) [![Apache License v2.0](https://img.shields.io/badge/License-Apache%202.0-brightgreen.svg)](https://github.com/VenomVendor/NullDefense/blob/master/LICENSE) [![Latest Version](https://img.shields.io/maven-metadata/v/https/jcenter.bintray.com/com/venomvendor/library/gson-nulldefense/maven-metadata.xml.svg)](https://bintray.com/venomvendor/maven/NullDefense)
[![Build Status](https://img.shields.io/travis/VenomVendor/NullDefense/master.svg?logo=travis)](https://travis-ci.org/VenomVendor/NullDefense)
[![Apache License v2.0](https://img.shields.io/badge/License-Apache%202.0-brightgreen.svg)](https://github.com/VenomVendor/NullDefense/blob/master/LICENSE)
[![Latest Version](https://img.shields.io/maven-metadata/v/https/jcenter.bintray.com/com/venomvendor/gson-nulldefense/maven-metadata.xml.svg)](https://bintray.com/bintray/jcenter/NullDefense)

# Code Quality
[![codecov](https://codecov.io/gh/VenomVendor/NullDefense/branch/master/graph/badge.svg)](https://codecov.io/gh/VenomVendor/NullDefense) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/f067e5c9a9c14c53843bc56f0669d993)](https://www.codacy.com/app/VenomVendor/NullDefense?utm_source=github.com&utm_medium=referral&utm_content=VenomVendor/NullDefense&utm_campaign=Badge_Grade) [![Codacy Badge](https://api.codacy.com/project/badge/Coverage/f067e5c9a9c14c53843bc56f0669d993)](https://www.codacy.com/app/VenomVendor/NullDefense?utm_source=github.com&utm_medium=referral&utm_content=VenomVendor/NullDefense&utm_campaign=Badge_Coverage) [![codebeat badge](https://codebeat.co/badges/ef5996c2-d284-454e-a497-f5438f8867e7)](https://codebeat.co/projects/github-com-venomvendor-nulldefense-master) [![CodeFactor](https://www.codefactor.io/repository/github/venomvendor/nulldefense/badge)](https://www.codefactor.io/repository/github/venomvendor/nulldefense) [![Maintainability](https://api.codeclimate.com/v1/badges/12788f6de414f39eb749/maintainability)](https://codeclimate.com/github/VenomVendor/NullDefense/maintainability) [![Test Coverage](https://api.codeclimate.com/v1/badges/12788f6de414f39eb749/test_coverage)](https://codeclimate.com/github/VenomVendor/NullDefense/test_coverage)
[![Codecov](https://codecov.io/gh/VenomVendor/NullDefense/branch/master/graph/badge.svg)](https://codecov.io/gh/VenomVendor/NullDefense)
[![Codacy Grade](https://api.codacy.com/project/badge/Grade/f067e5c9a9c14c53843bc56f0669d993)](https://www.codacy.com/app/VenomVendor/NullDefense?utm_source=github.com&utm_medium=referral&utm_content=VenomVendor/NullDefense&utm_campaign=Badge_Grade)
[![Codacy Coverage](https://api.codacy.com/project/badge/Coverage/f067e5c9a9c14c53843bc56f0669d993)](https://www.codacy.com/app/VenomVendor/NullDefense?utm_source=github.com&utm_medium=referral&utm_content=VenomVendor/NullDefense&utm_campaign=Badge_Coverage)
[![Codebeat](https://codebeat.co/badges/ef5996c2-d284-454e-a497-f5438f8867e7)](https://codebeat.co/projects/github-com-venomvendor-nulldefense-master)
[![CodeFactor](https://www.codefactor.io/repository/github/venomvendor/nulldefense/badge)](https://www.codefactor.io/repository/github/venomvendor/nulldefense)
[![Codeclimate Maintainability](https://api.codeclimate.com/v1/badges/12788f6de414f39eb749/maintainability)](https://codeclimate.com/github/VenomVendor/NullDefense/maintainability)
[![Codeclimate Coverage](https://api.codeclimate.com/v1/badges/12788f6de414f39eb749/test_coverage)](https://codeclimate.com/github/VenomVendor/NullDefense/test_coverage)

-----------

Expand All @@ -19,4 +27,4 @@

-----------

### More at [HomePage⬈](https://venomvendor.github.io/NullDefense)
### Know more at [HomePage⬈](https://venomvendor.github.io/NullDefense)
24 changes: 19 additions & 5 deletions RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,31 @@
Release Notes Sample
New Line is created by adding TWO whitespace at EOL
[//]: ## vMajor.Minor.Patch
[//]: **Date**: dd-MMM-yyyy→→
[//]: **Fixes**: #1, #2, #5→→
[//]: **Date**: dd-MMM-yyyy→→
[//]: **Fixes**: #1, #2, #5→→
[//]: **Notes**
[//]: - Notes 1
[//]: - Notes 2
)

# Release Notes

## v1.0.0
**Date**: 26-Jul-2018
**Fixes**: N/A
## [v1.0.1](https://github.com/VenomVendor/NullDefense/releases/tag/v1.0.1)
**Date**: 27-Jul-2018
**Fixes**: [#16](https://github.com/VenomVendor/NullDefense/issues/16)
**Notes**
- Fixed #16 causing crash during serialization

## [v1.0.0](https://github.com/VenomVendor/NullDefense/releases/tag/v1.0.0)
**Date**: 26-Jul-2018
**Fixes**: [#6](https://github.com/VenomVendor/NullDefense/issues/6),
[#7](https://github.com/VenomVendor/NullDefense/issues/7),
[#8](https://github.com/VenomVendor/NullDefense/issues/8)
**Notes**
- Initial Release

## [v1.0.0-beta](https://github.com/VenomVendor/NullDefense/releases/tag/v1.0.0-beta)
**Date**: 26-Jul-2018
**Fixes**: N/A
**Notes**
- Pre Release
1 change: 0 additions & 1 deletion dependencies.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ val testImplementation by configurations

dependencies {
implementation("com.google.code.gson:gson:2.8.5")
implementation("com.google.code.findbugs:jsr305:3.0.2")

testImplementation("org.junit.jupiter:junit-jupiter-engine:5.2.0")
testImplementation("org.junit.platform:junit-platform-runner:1.2.0")
Expand Down
1 change: 0 additions & 1 deletion proguard.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
-outjars gson-nulldefense.min.jar

-libraryjars '/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/lib/rt.jar'
-libraryjars jsr305-3.0.2.jar
-libraryjars gson-2.8.5.jar

-overloadaggressively
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,15 @@
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;

import org.jetbrains.annotations.NotNull;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Collections;
import java.util.Objects;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

/**
* Adapter for removing <b>null</b> objects &amp; <b>empty</b> Collection once the object is created.
Expand Down Expand Up @@ -88,12 +85,19 @@ public final class NullDefenseTypeAdapterFactory implements TypeAdapterFactory {
private boolean discardEmpty;

/**
* Requires annotated class type for checking fields with annotation
* Requires annotated class for checking fields with annotation.
* <p>Example</p>
* <pre>
* &#064;Retention(RetentionPolicy.RUNTIME)
* &#064;Target({ElementType.FIELD})
* public @interface Mandatory { }
* </pre>
*
* @param annotatedType Class used for marking fields as mandatory
* @throws NullPointerException {@inheritDoc}
* @param annotatedType Class used for marking fields as mandatory,
* this has to be of retention type {@link RetentionPolicy#RUNTIME}
* @throws NullPointerException if annotated class is null
*/
public NullDefenseTypeAdapterFactory(@Nonnull Class<? extends Annotation> annotatedType) {
public NullDefenseTypeAdapterFactory(Class<? extends Annotation> annotatedType) {
Objects.requireNonNull(annotatedType, "Annotation class cannot be null");
this.annotatedType = annotatedType;
removeEmptyCollection();
Expand Down Expand Up @@ -143,8 +147,8 @@ private static final class DefensiveAdapter<T> extends TypeAdapter<T> {
/* When true, Collection#size() == 0 is removed */
private final boolean discardEmpty;

DefensiveAdapter(@Nonnull TypeAdapter<T> author, boolean discardEmpty,
@Nonnull Class<? extends Annotation> annotatedType) {
DefensiveAdapter(TypeAdapter<T> author, boolean discardEmpty,
Class<? extends Annotation> annotatedType) {
Objects.requireNonNull(author, "TypeAdapter cannot be null");
Objects.requireNonNull(annotatedType, "Annotation cannot be null");
this.author = author;
Expand All @@ -154,9 +158,7 @@ private static final class DefensiveAdapter<T> extends TypeAdapter<T> {

@Override
public void write(JsonWriter out, T value) throws IOException {
if (value != null) {
author.write(out, value);
}
author.write(out, value);
}

@Override
Expand All @@ -183,7 +185,7 @@ public T read(JsonReader reader) throws IOException {
* @param result data to process
* @return same result if not null or conditional empty, else {@code null}
*/
private T getFilteredData(@Nonnull T result) {
private T getFilteredData(T result) {
for (Field field : result.getClass().getDeclaredFields()) {
if (field.getType().isPrimitive()) {
// Skip primitives
Expand All @@ -199,42 +201,53 @@ private T getFilteredData(@Nonnull T result) {
}

/**
* Check if data contains null or empty objects
* Check if data contains null or empty objects only on annotated fields
*
* @param result data to process
* @param field declared variable in current object
* @return {@code true} if data is invalid
*/
private boolean containsInvalidData(@Nonnull T result, @Nonnull Field field) {
private boolean containsInvalidData(T result, Field field) {
// Check if current field is marked
if (field.isAnnotationPresent(annotatedType)) {
// To read private fields
field.setAccessible(true);

Object value = null;
try {
// Lil, costly operation.
value = field.get(result);
} catch (IllegalAccessException ex) {
ex.printStackTrace();
}

// Check of emptyness
if (isEmpty(value)) {
if (hasInvalidData(result, field)) {
return true;
}
}
// Data is valid
return false;
}

/**
* Check if data contains null or empty objects
*
* @param result data to process
* @param field declared variable in current object
* @return {@code true} if data is invalid
*/
private boolean hasInvalidData(T result, Field field) {
Object value = null;
try {
// Lil, costly operation.
value = field.get(result);
} catch (IllegalAccessException ex) {
ex.printStackTrace();
}

// Check of emptyness
return isEmpty(value);
}

/**
* Checks if data is either null or empty
*
* @param value data to process
* @return {@code true} if data is invalid
*/
private boolean isEmpty(@Nullable Object value) {
private boolean isEmpty(Object value) {
return value == null || isEmptyCollection(value);
}

Expand All @@ -245,7 +258,7 @@ private boolean isEmpty(@Nullable Object value) {
* @param value data to process
* @return {@code true} if data is invalid
*/
private boolean isEmptyCollection(@NotNull Object value) {
private boolean isEmptyCollection(Object value) {
if (value instanceof Collection) {
Collection subCollection = ((Collection) value);
// Cost is O(N^2), due to rearrangement
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,11 +173,7 @@ void shouldThrowExceptionForMissingChildren() {
ParentTest parent = parser.fromJson("{\"name\":\"VenomVendor\"}", ParentTest.class);
assertNotNull(parent);

IllegalStateException exception = assertThrows(IllegalStateException.class, () ->
defensiveParser.toJson(parent, ParentTest.class));

assertTrue(exception.getMessage().contains("children"),
"Expecting `children`, as children is mandatory");
assertTrue(defensiveParser.toJson(parent, ParentTest.class).contains("children"));
});
}

Expand Down
2 changes: 1 addition & 1 deletion version.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
extra["major"] = 1
extra["minor"] = 0
extra["patch"] = 0
extra["patch"] = 1

val suffix = ""
val version = "${extra["major"]}.${extra["minor"]}.${extra["patch"]}"
Expand Down

0 comments on commit 52c73d4

Please sign in to comment.