• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

trydofor / professional-wings / #137

03 Feb 2025 03:30PM UTC coverage: 63.633% (-0.07%) from 63.705%
#137

push

trydofor
✨ I18nMessage compatible config #312

18 of 23 new or added lines in 5 files covered. (78.26%)

30 existing lines in 2 files now uncovered.

12936 of 20329 relevant lines covered (63.63%)

0.64 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.3
/wings/slardar/src/main/java/pro/fessional/wings/slardar/jackson/EmptyValuePropertyFilter.java
1
package pro.fessional.wings.slardar.jackson;
2

3
import com.fasterxml.jackson.annotation.JsonFilter;
4
import com.fasterxml.jackson.core.JsonGenerator;
5
import com.fasterxml.jackson.databind.JavaType;
6
import com.fasterxml.jackson.databind.SerializerProvider;
7
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
8
import com.fasterxml.jackson.databind.ser.PropertyWriter;
9
import lombok.extern.slf4j.Slf4j;
10
import org.jetbrains.annotations.Nullable;
11
import pro.fessional.mirana.time.ThreadNow;
12

13
import java.lang.reflect.Array;
14
import java.time.LocalDate;
15
import java.time.LocalDateTime;
16
import java.time.OffsetDateTime;
17
import java.time.ZonedDateTime;
18
import java.util.Collection;
19
import java.util.Date;
20
import java.util.Map;
21

22
/**
23
 * <pre>
24
 * no output if LocalDate, LocalDateTime, ZonedDateTime or OffsetDateTime is `empty`.
25
 * no output if Array, Collection or Map is `empty`.
26
 * </pre>
27
 *
28
 * @author trydofor
29
 * @since 2021-10-28
30
 */
31
@Slf4j
1✔
32
public class EmptyValuePropertyFilter implements AutoRegisterPropertyFilter {
33

34
    public static final String Id = "EmptyValue";
35

36
    @JsonFilter(Id)
37
    public static class EmptyValueMixin {
×
38
    }
39
    // must not (1) interface (2) abstract class (3) JavaTimeModule
40
    // https://github.com/FasterXML/jackson-docs/wiki/JacksonMixInAnnotations
41
    public static final Class<?> MixinClass = Object.class;
1✔
42

43
    private final LocalDate emptyDate;
44
    private final LocalDateTime emptyDateMin;
45
    private final LocalDateTime emptyDateMax;
46
    private final boolean emptyList;
47
    private final boolean emptyMap;
48

49
    public EmptyValuePropertyFilter(@Nullable LocalDate emptyDate, int offset, boolean list, boolean map) {
1✔
50
        this.emptyDate = emptyDate;
1✔
51
        if (emptyDate != null) {
1✔
52
            final LocalDateTime dt = emptyDate.atStartOfDay();
1✔
53
            if (offset != 0) {
1✔
54
                this.emptyDateMin = dt.minusHours(offset);
1✔
55
                this.emptyDateMax = dt.plusHours(offset);
1✔
56
            }
57
            else {
58
                this.emptyDateMin = null;
×
59
                this.emptyDateMax = null;
×
60
            }
61
        }
1✔
62
        else {
63
            this.emptyDateMin = null;
×
64
            this.emptyDateMax = null;
×
65
        }
66
        this.emptyList = list;
1✔
67
        this.emptyMap = map;
1✔
68
    }
1✔
69

70
    @Override
71
    public String getId() {
72
        return Id;
1✔
73
    }
74

75
    @Override
76
    public void serializeAsField(Object pojo, JsonGenerator gen, SerializerProvider prov, PropertyWriter writer) throws Exception {
77
        if (writer instanceof BeanPropertyWriter wt) {
1✔
78
            try {
79
                final JavaType rt = wt.getType();
1✔
80
                if ((emptyDate != null && dealEmptyDate(pojo, gen, prov, wt, rt)) ||
1✔
81
                    (emptyList && dealEmptyList(pojo, gen, prov, wt, rt)) ||
1✔
82
                    (emptyMap && dealEmptyMap(pojo, gen, prov, wt, rt))) {
1✔
83
                    return;
1✔
84
                }
85
            }
86
            catch (Exception ex) {
×
87
                if (log.isDebugEnabled()) {
×
88
                    // noinspection StringConcatenationArgumentToLogCall
89
                    log.debug("Skipping '" + writer.getFullName() + "' on '" + pojo.getClass().getName()
×
90
                              + "' as an exception was thrown when retrieving its value", ex);
91
                }
92
            }
1✔
93
        }
94
        writer.serializeAsField(pojo, gen, prov);
1✔
95
    }
1✔
96

97
    private boolean dealEmptyList(Object pojo, JsonGenerator gen, SerializerProvider prov, BeanPropertyWriter writer, JavaType rt) throws Exception {
98
        final boolean isArr = rt.isArrayType();
1✔
99
        if ((isArr || rt.isTypeOrSubTypeOf(Collection.class))) {
1✔
100
            final Object v = writer.get(pojo);
1✔
101
            if ((v instanceof Collection && ((Collection<?>) v).isEmpty()) ||
1✔
102
                (isArr && v != null && Array.getLength(v) == 0)) {
1✔
103
                return skipEmpty(pojo, gen, prov, writer);
1✔
104
            }
105
        }
106

107
        return false;
1✔
108
    }
109

110
    private boolean dealEmptyMap(Object pojo, JsonGenerator gen, SerializerProvider prov, BeanPropertyWriter writer, JavaType rt) throws Exception {
111
        if (rt.isTypeOrSubTypeOf(Map.class)) {
1✔
112
            final Object v = writer.get(pojo);
1✔
113
            if (v instanceof Map && ((Map<?, ?>) v).isEmpty()) {
1✔
114
                return skipEmpty(pojo, gen, prov, writer);
1✔
115
            }
116
        }
117
        return false;
1✔
118
    }
119

120
    private boolean dealEmptyDate(Object pojo, JsonGenerator gen, SerializerProvider prov, BeanPropertyWriter writer, JavaType rt) throws Exception {
121
        if ((rt.isTypeOrSubTypeOf(LocalDate.class) ||
1✔
122
             rt.isTypeOrSubTypeOf(LocalDateTime.class) ||
1✔
123
             rt.isTypeOrSubTypeOf(ZonedDateTime.class) ||
1✔
124
             rt.isTypeOrSubTypeOf(OffsetDateTime.class) ||
1✔
125
             rt.isTypeOrSubTypeOf(Date.class))
1✔
126
        ) {
127
            final Object v = writer.get(pojo);
1✔
128
            if ((v instanceof LocalDate lt && emptyDate(lt)) ||
1✔
129
                (v instanceof LocalDateTime ldt && emptyDateTime(ldt)) ||
1✔
130
                (v instanceof ZonedDateTime zdt && emptyDateTime(zdt.toLocalDateTime())) ||
1✔
131
                (v instanceof OffsetDateTime odt && emptyDateTime(odt.toLocalDateTime())) ||
1✔
NEW
132
                (v instanceof Date dt && emptyDateTime(dt))
×
133
            ) {
134
                return skipEmpty(pojo, gen, prov, writer);
1✔
135
            }
136
        }
137
        return false;
1✔
138
    }
139

140
    private boolean emptyDate(LocalDate d) {
141
        return emptyDate.equals(d);
1✔
142
    }
143

144
    private boolean emptyDateTime(Date dt) {
145
        final LocalDateTime ldt = dt.toInstant().atZone(ThreadNow.sysZoneId()).toLocalDateTime();
×
146
        return emptyDateTime(ldt);
×
147
    }
148

149
    // Considering timezone, the difference is considered equal within `offset` hours.
150
    private boolean emptyDateTime(LocalDateTime dt) {
151
        if (emptyDate.equals(dt.toLocalDate())) return true;
1✔
152
        if (emptyDateMin == null || emptyDateMax == null) {
1✔
153
            return false;
×
154
        }
155
        else {
156
            return !dt.isBefore(emptyDateMin) && !dt.isAfter(emptyDateMax);
1✔
157
        }
158
    }
159

160
    private boolean skipEmpty(Object pojo, JsonGenerator gen, SerializerProvider prov, BeanPropertyWriter writer) throws Exception {
161
        if (!gen.canOmitFields()) {
1✔
162
            writer.serializeAsOmittedField(pojo, gen, prov);
×
163
        }
164
        return true;
1✔
165
    }
166
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc