Skip to content

Commit 95b7adf

Browse files
committed
Implemented Jackson writing of flows.
1 parent 5c94989 commit 95b7adf

12 files changed

+359
-61
lines changed

CHANGES.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@
142142
ConfigurationLoader can now load XML, JSON, and Yaml into objects.
143143
</action>
144144
<action dev="essiembre" type="add">
145-
New PredicatedConsumer #isTrue and #isFalse methods.
145+
New PredicatedConsumer #isTrue and #isFalse and #elseConsumer methods.
146146
</action>
147147
<action dev="essiembre" type="add">
148148
New Predicates #anyOf and #allOf methods.

TODO.txt

-30
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,6 @@
22
TODO:
33
==============
44

5-
- Maybe: replace json with jackson?
6-
7-
- Consider integration with GitHub Actions and deploy zips there?
8-
95
- Add {@nx.block.hidden that would hide in javadoc, but eligible for includes
106
in other classes.
117

@@ -16,14 +12,9 @@ TODO:
1612
- Add equiv. of "trimToNull" to "PropertyMatchers" so that blank values
1713
could be treated as null (i.e., non-existant).
1814

19-
- Allow specifying a base package from which to scan for classes when using
20-
ClassFinder.
21-
2215
- Maybe: have CachedStreamFactory set itself on current thread (but not
2316
overwriting it on parent thread).
2417

25-
- XSD: Always have a type and an element extending it to improve reuse.
26-
2718
- Modify Jar copier to handle cases where snapshot are timestamped instead and
2819
not being considered the latest when they should. Like:
2920
norconex-commons-lang-1.13.0-20170328.184247-17.jar vs
@@ -41,16 +32,10 @@ LOWER:
4132
- Use nill/nillable for null handling, as desribed here:
4233
https://kiranscope.blogspot.com/2009/03/blank-space-or-null-string-validation.html
4334

44-
- Have loadFromXML return validationError list.
45-
4635
- DONE? Add feature and tests for when parent element is specified but has no
4736
value, which means blanking a list
4837
(e.g. HTTP Collector: StandardSitemapResolverTest).
4938

50-
- Make method naming more consistent between Properties and XML.
51-
52-
- Overload Properties load/store methods to accept a Path
53-
5439
- Make Properties and XML take a IConverter as constructor agreement, but
5540
default to Converter static instance.
5641

@@ -67,23 +52,8 @@ LOWER:
6752
MAYBE/IDEAS:
6853
--------------
6954

70-
- Make deployment part of CICD tool.
71-
7255
- MAYBE modify assembly to use licenses.xml instead of THIRD-PARTY?
7356
(the second is easier to read for non-techies)
7457

75-
- Add XML.getValue(String xpath, Class<T> type) and use Convert
76-
for conversion. Remove getXXX Methods? Do same with Set if not done already.
77-
78-
- Same as above, but for Properties
79-
80-
- Maybe: See if we can have getElement() use return type of assigned variable
81-
to determine how to create the object instead of having getXXX methods?
82-
83-
- Have option to not do validation on loading of dom, in case a consuming
84-
class wants to modify the DOM before validation.
85-
86-
- Remove all @since 1.x???
87-
8858
- Reduce dependencies wherever possible.
8959

src/main/java/com/norconex/commons/lang/flow/module/ConditionGroupHandler.java

+69-2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
import com.fasterxml.jackson.core.JsonToken;
2424
import com.norconex.commons.lang.flow.module.FlowDeserializer.FlowDeserContext;
25+
import com.norconex.commons.lang.flow.module.FlowSerializer.FlowSerContext;
2526
import com.norconex.commons.lang.function.Predicates;
2627

2728
import lombok.Data;
@@ -75,9 +76,75 @@ private void readObject(FlowDeserContext ctx, Predicates<T> preds)
7576
FlowUtil.logClose(ctx, p.getCurrentName());
7677
}
7778

79+
@SuppressWarnings("unchecked")
7880
@Override
79-
public void write() throws IOException {
80-
// TODO Auto-generated method stub
81+
public void write(Predicate<T> predicate, FlowSerContext ctx)
82+
throws IOException {
83+
var gen = ctx.getGen();
84+
85+
gen.writeFieldName(isAny() ? "anyOf" : "allOf");
86+
// gen.writeStartObject();
87+
88+
var predicateGroup = (Predicates<T>) predicate;
89+
gen.writeStartArray();
90+
for (Predicate<T> pred : predicateGroup) {
91+
gen.writeStartObject();
92+
if (pred instanceof Predicates<T> chidPredGroup) {
93+
if (chidPredGroup.isAny()) {
94+
// anyOf
95+
((ConditionGroupHandler<T>) Statement.ANYOF.handler())
96+
.write(chidPredGroup, ctx);
97+
} else {
98+
// allOf
99+
((ConditionGroupHandler<T>) Statement.ALLOF.handler())
100+
.write(chidPredGroup, ctx);
101+
}
102+
} else {
103+
// condition
104+
((ConditionHandler<T>) Statement.CONDITION.handler()).write(
105+
pred, ctx);
106+
}
107+
gen.writeEndObject();
108+
}
109+
gen.writeEndArray();
110+
111+
112+
113+
// if (predicate instanceof Predicates<T> predicateGroup) {
114+
// if (predicateGroup.isAny()) {
115+
// // anyOf
116+
// ((ConditionGroupHandler<T>) Statement.ANYOF.handler()).write(
117+
// predicateGroup, ctx);
118+
// } else {
119+
// // allOf
120+
// ((ConditionGroupHandler<T>) Statement.ALLOF.handler()).write(
121+
// predicateGroup, ctx);
122+
// }
123+
// } else {
124+
// // condition
125+
// ((ConditionHandler<T>) Statement.CONDITION.handler()).write(
126+
// predicate, ctx);
127+
// }
128+
129+
// gen.writeEndObject();
81130

131+
// var preds = (Predicates<T>) obj;
132+
// for (Predicate<T> pred : preds) {
133+
// if (pred instanceof Predicates<T> pgroup) {
134+
// if (pgroup.isAny()) {
135+
// gen.writeFieldName(Statement.ANYOF.toString());
136+
// ((ConditionGroupHandler<T>) Statement.ANYOF.handler()).write(
137+
// pgroup, ctx);
138+
// } else {
139+
// gen.writeFieldName(Statement.ALLOF.toString());
140+
// ((ConditionGroupHandler<T>) Statement.ALLOF.handler()).write(
141+
// pgroup, ctx);
142+
// }
143+
// } else {
144+
// gen.writeFieldName(Statement.CONDITION.toString());
145+
// ((ConditionHandler<T>) Statement.CONDITION.handler()).write(
146+
// pred, ctx);
147+
// }
148+
// }
82149
}
83150
}

src/main/java/com/norconex/commons/lang/flow/module/ConditionHandler.java

+5-3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import com.norconex.commons.lang.ClassUtil;
2222
import com.norconex.commons.lang.flow.FlowPredicateAdapter;
2323
import com.norconex.commons.lang.flow.module.FlowDeserializer.FlowDeserContext;
24+
import com.norconex.commons.lang.flow.module.FlowSerializer.FlowSerContext;
2425

2526
/**
2627
* Handles flow conditions.
@@ -66,8 +67,9 @@ public Predicate<T> read(FlowDeserContext ctx) throws IOException {
6667
}
6768

6869
@Override
69-
public void write() throws IOException {
70-
//TODO
70+
public void write(Predicate<T> obj, FlowSerContext ctx) throws IOException {
71+
var gen = ctx.getGen();
72+
gen.writeFieldName(Statement.CONDITION.toString());
73+
gen.writeObject(obj);
7174
}
72-
7375
}

src/main/java/com/norconex/commons/lang/flow/module/FlowModule.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public class FlowModule extends SimpleModule {
2525
private static final long serialVersionUID = 1L;
2626

2727
public FlowModule(FlowMapperConfig config) {
28-
//TODO addSerializer(Flow_MAYBE.class, new FlowSerializer<>(config));
28+
setSerializerModifier(new FlowSerializerModifier(config));
2929
setDeserializerModifier(new FlowDeserializerModifier(config));
3030
}
3131
}

src/main/java/com/norconex/commons/lang/flow/module/FlowSerializer.java

+49-6
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,16 @@
1818
import java.util.function.Consumer;
1919

2020
import com.fasterxml.jackson.core.JsonGenerator;
21+
import com.fasterxml.jackson.databind.BeanProperty;
22+
import com.fasterxml.jackson.databind.JsonMappingException;
2123
import com.fasterxml.jackson.databind.JsonSerializer;
2224
import com.fasterxml.jackson.databind.SerializerProvider;
25+
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
26+
import com.fasterxml.jackson.databind.ser.ResolvableSerializer;
2327
import com.norconex.commons.lang.flow.FlowMapperConfig;
28+
import com.norconex.commons.lang.flow.JsonFlow;
2429

30+
import lombok.Data;
2531
import lombok.RequiredArgsConstructor;
2632

2733
/**
@@ -31,17 +37,54 @@
3137
* @since 3.0.0
3238
*/
3339
@RequiredArgsConstructor
34-
public class FlowSerializer<F extends Consumer<T>, T>
35-
extends JsonSerializer<F> {
40+
public class FlowSerializer<T> extends JsonSerializer<Consumer<T>>
41+
implements ContextualSerializer, ResolvableSerializer {
3642

3743
private final FlowMapperConfig config;
44+
private final JsonSerializer<?> defaultSerializer;
45+
46+
@SuppressWarnings("unchecked")
47+
private RootHandler<T> rootHandler =
48+
(RootHandler<T>) Statement.THEN.handler();
3849

3950
@Override
40-
public void serialize(F value,
41-
JsonGenerator gen, SerializerProvider sp) throws IOException {
42-
// gen.writeString(GenericConverter.convert(value));
51+
public void serialize(
52+
Consumer<T> value,
53+
JsonGenerator gen,
54+
SerializerProvider sp) throws IOException {
55+
rootHandler.write(value, new FlowSerContext(config, gen));
56+
}
4357

44-
//TODO ensure order is preserved when writing
58+
@Override
59+
public JsonSerializer<?> createContextual(
60+
SerializerProvider prov, BeanProperty property)
61+
throws JsonMappingException {
62+
if (property == null) {
63+
return defaultSerializer;
64+
}
65+
return property.getAnnotation(JsonFlow.class) == null
66+
? defaultSerializer : this;
67+
}
68+
69+
@Override
70+
public void resolve(SerializerProvider provider)
71+
throws JsonMappingException {
72+
if (defaultSerializer != null
73+
&& defaultSerializer instanceof ResolvableSerializer rs) {
74+
rs.resolve(provider);
75+
}
76+
}
4577

78+
@Data
79+
static class FlowSerContext {
80+
private final FlowMapperConfig config;
81+
private final JsonGenerator gen;
82+
private int depth;
83+
int incrementDepth() {
84+
return depth++;
85+
}
86+
int decrementDepth() {
87+
return --depth;
88+
}
4689
}
4790
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/* Copyright 2023 Norconex Inc.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
package com.norconex.commons.lang.flow.module;
16+
17+
import java.util.function.Consumer;
18+
19+
import com.fasterxml.jackson.databind.BeanDescription;
20+
import com.fasterxml.jackson.databind.JsonSerializer;
21+
import com.fasterxml.jackson.databind.SerializationConfig;
22+
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
23+
import com.norconex.commons.lang.flow.FlowMapperConfig;
24+
25+
import lombok.RequiredArgsConstructor;
26+
27+
@RequiredArgsConstructor
28+
public class FlowSerializerModifier extends BeanSerializerModifier {
29+
30+
private final FlowMapperConfig flowMapperConfig;
31+
32+
@Override
33+
public JsonSerializer<?> modifySerializer(
34+
SerializationConfig config,
35+
BeanDescription beanDesc,
36+
JsonSerializer<?> serializer) {
37+
38+
Class<?> consumerType = flowMapperConfig.getConsumerType().getBaseType();
39+
if (consumerType != null
40+
&& consumerType.getClass().isAssignableFrom(
41+
beanDesc.getBeanClass())
42+
|| Consumer.class.isAssignableFrom(beanDesc.getBeanClass())) {
43+
return new FlowSerializer<>(flowMapperConfig, serializer);
44+
}
45+
return super.modifySerializer(config, beanDesc, serializer);
46+
}
47+
}

src/main/java/com/norconex/commons/lang/flow/module/IfHandler.java

+47-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020

2121
import com.fasterxml.jackson.core.JsonToken;
2222
import com.norconex.commons.lang.flow.module.FlowDeserializer.FlowDeserContext;
23+
import com.norconex.commons.lang.flow.module.FlowSerializer.FlowSerContext;
2324
import com.norconex.commons.lang.function.PredicatedConsumer;
25+
import com.norconex.commons.lang.function.Predicates;
2426

2527
import lombok.Data;
2628
import lombok.RequiredArgsConstructor;
@@ -78,9 +80,52 @@ public Consumer<T> read(FlowDeserContext ctx)
7880
negate);
7981
}
8082

83+
@SuppressWarnings("unchecked")
8184
@Override
82-
public void write() throws IOException {
83-
//TODO
85+
public void write(Consumer<T> obj, FlowSerContext ctx) throws IOException {
86+
87+
var gen = ctx.getGen();
88+
gen.writeStartObject();
89+
gen.writeFieldName(negate ? "ifNot" : "if");
90+
gen.writeStartObject();
91+
92+
var predicatedConsumer = (PredicatedConsumer<T>) obj;
93+
var predicate = predicatedConsumer.getPredicate();
94+
95+
if (predicate instanceof Predicates<T> predicateGroup) {
96+
if (predicateGroup.isAny()) {
97+
// anyOf
98+
((ConditionGroupHandler<T>) Statement.ANYOF.handler()).write(
99+
predicateGroup, ctx);
100+
} else {
101+
// allOf
102+
((ConditionGroupHandler<T>) Statement.ALLOF.handler()).write(
103+
predicateGroup, ctx);
104+
}
105+
} else {
106+
// condition
107+
((ConditionHandler<T>) Statement.CONDITION.handler()).write(
108+
predicatedConsumer.getPredicate(), ctx);
109+
}
110+
111+
// then
112+
gen.writeFieldName(Statement.THEN.toString());
113+
gen.writeStartArray();
114+
((RootHandler<T>) Statement.THEN.handler()).write(
115+
predicatedConsumer.getThenConsumer(), ctx);
116+
gen.writeEndArray();
117+
118+
// else
119+
if (predicatedConsumer.getElseConsumer() != null) {
120+
gen.writeFieldName(Statement.ELSE.toString());
121+
gen.writeStartArray();
122+
((RootHandler<T>) Statement.ELSE.handler()).write(
123+
predicatedConsumer.getElseConsumer(), ctx);
124+
gen.writeEndArray();
125+
}
126+
127+
gen.writeEndObject();
128+
gen.writeEndObject();
84129
}
85130

86131
@SuppressWarnings("unchecked")

0 commit comments

Comments
 (0)