Skip to content

Commit 9070568

Browse files
committed
add something like mixin
1 parent d4af5a5 commit 9070568

File tree

7 files changed

+132
-11
lines changed

7 files changed

+132
-11
lines changed

jsonb-generator/src/main/java/io/avaje/jsonb/generator/BeanReader.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,27 @@ class BeanReader {
4242
this.constructor = typeReader.constructor();
4343
}
4444

45-
@Override
45+
public BeanReader(
46+
TypeElement beanType,
47+
Map<String, Element> mixInFields,
48+
ProcessingContext context) {
49+
50+
this.beanType = beanType;
51+
this.type = beanType.getQualifiedName().toString();
52+
this.shortName = shortName(beanType);
53+
final NamingConventionReader ncReader = new NamingConventionReader(beanType);
54+
this.namingConvention = ncReader.get();
55+
this.typeProperty = ncReader.typeProperty();
56+
this.typeReader = new TypeReader(beanType, mixInFields, context, namingConvention);
57+
typeReader.process();
58+
this.nonAccessibleField = typeReader.nonAccessibleField();
59+
this.hasSubTypes = typeReader.hasSubTypes();
60+
this.allFields = typeReader.allFields();
61+
this.constructor = typeReader.constructor();
62+
63+
}
64+
65+
@Override
4666
public String toString() {
4767
return beanType.toString();
4868
}

jsonb-generator/src/main/java/io/avaje/jsonb/generator/ImportReader.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
class ImportReader {
1212

1313
private static final String JSON_IMPORT = "io.avaje.jsonb.Json.Import";
14+
private static final String JSON_MIXIN = "io.avaje.jsonb.Json.MixIn";
1415

1516
/**
1617
* Read the Json.Import annotation using annotation mirrors.
@@ -31,4 +32,15 @@ List<String> read(Element element) {
3132
return fullNames;
3233
}
3334

35+
String readMixin(Element element) {
36+
for (final AnnotationMirror mirror : element.getAnnotationMirrors()) {
37+
if (JSON_MIXIN.equals(mirror.getAnnotationType().toString())) {
38+
for (final Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry :
39+
mirror.getElementValues().entrySet()) {
40+
return Util.trimClassSuffix(entry.getValue().toString());
41+
}
42+
}
43+
}
44+
return null;
45+
}
3446
}

jsonb-generator/src/main/java/io/avaje/jsonb/generator/Processor.java

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ public Set<String> getSupportedAnnotationTypes() {
4343
Set<String> annotations = new LinkedHashSet<>();
4444
annotations.add(Json.class.getCanonicalName());
4545
annotations.add(Json.Import.class.getCanonicalName());
46+
annotations.add(Json.MixIn.class.getCanonicalName());
4647
return annotations;
4748
}
4849

@@ -111,12 +112,24 @@ private boolean ignoreType(String type) {
111112
/**
112113
* Elements that have a {@code @Json.Import} annotation.
113114
*/
114-
private void writeAdaptersForImported(Set<? extends Element> importedElements) {
115-
for (Element importedElement : importedElements) {
116-
for (String importType : importReader.read(importedElement)) {
117-
TypeElement element = context.element(importType);
115+
private void writeAdaptersForImported(
116+
Set<? extends Element> importedElements, Set<? extends Element> mixed) {
117+
final Map<String, TypeElement> mixinMap = new HashMap<>();
118+
for (final Element mixin : mixed) {
119+
final String importType = importReader.readMixin(mixin);
120+
if (importType != null) {
121+
mixinMap.put(importType, context.element(mixin.asType().toString()));
122+
}
123+
}
124+
125+
for (final Element importedElement : importedElements) {
126+
for (final String importType : importReader.read(importedElement)) {
127+
final TypeElement element = context.element(importType);
128+
118129
if (element == null) {
119130
context.logError("Unable to find imported element " + importType);
131+
} else if (mixinMap.containsKey(importType)) {
132+
writeAdapterForMixInType(element, mixinMap.get(importType));
120133
} else {
121134
writeAdapterForType(element);
122135
}
@@ -158,7 +171,20 @@ private void writeAdapters(Set<? extends Element> beans) {
158171
}
159172

160173
private void writeAdapterForType(TypeElement typeElement) {
161-
BeanReader beanReader = new BeanReader(typeElement, context);
174+
final BeanReader beanReader = new BeanReader(typeElement, context);
175+
writeAdapter(typeElement, beanReader);
176+
}
177+
178+
private void writeAdapterForMixInType(TypeElement typeElement, TypeElement mixin) {
179+
final Map<String, Element> mixInFields =
180+
mixin.getEnclosedElements().stream()
181+
.filter(e -> e.getKind() == ElementKind.FIELD)
182+
.collect(Collectors.toMap(e -> e.getSimpleName().toString(), e -> e));
183+
final BeanReader beanReader = new BeanReader(typeElement, mixInFields, context);
184+
writeAdapter(typeElement, beanReader);
185+
}
186+
187+
private void writeAdapter(TypeElement typeElement, BeanReader beanReader) {
162188
beanReader.read();
163189
if (beanReader.nonAccessibleField()) {
164190
if (beanReader.hasJsonAnnotation()) {
@@ -176,5 +202,4 @@ private void writeAdapterForType(TypeElement typeElement) {
176202
context.logError("Error writing JsonAdapter for %s %s", beanReader, e);
177203
}
178204
}
179-
180205
}

jsonb-generator/src/main/java/io/avaje/jsonb/generator/TypeReader.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,26 @@ class TypeReader {
3232
private TypeSubTypeMeta currentSubType;
3333
private boolean nonAccessibleField;
3434

35+
private final Map<String, Element> mixInFields;
36+
3537
TypeReader(TypeElement baseType, ProcessingContext context, NamingConvention namingConvention) {
3638
this.baseType = baseType;
3739
this.context = context;
40+
this.mixInFields = new HashMap<>();
41+
this.namingConvention = namingConvention;
42+
this.hasJsonAnnotation = baseType.getAnnotation(Json.class) != null;
43+
this.subTypes = new TypeSubTypeReader(baseType, context);
44+
}
45+
46+
public TypeReader(
47+
TypeElement baseType,
48+
Map<String, Element> mixInFields,
49+
ProcessingContext context,
50+
NamingConvention namingConvention) {
51+
52+
this.baseType = baseType;
53+
this.mixInFields = mixInFields;
54+
this.context = context;
3855
this.namingConvention = namingConvention;
3956
this.hasJsonAnnotation = baseType.getAnnotation(Json.class) != null;
4057
this.subTypes = new TypeSubTypeReader(baseType, context);
@@ -74,7 +91,10 @@ void read(TypeElement type) {
7491
}
7592

7693
private void readField(Element element, List<FieldReader> localFields) {
77-
if (includeField(element)) {
94+
if (mixInFields.containsKey(element.getSimpleName().toString())) {
95+
element = mixInFields.get(element.getSimpleName().toString());
96+
}
97+
if (includeField(element)) {
7898
localFields.add(new FieldReader(element, namingConvention, currentSubType));
7999
}
80100
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package io.avaje.jsonb.generator.models.valid;
2+
3+
import io.avaje.jsonb.Json;
4+
5+
@Json.Import(MixinTarget.class)
6+
@Json.MixIn(MixinTarget.class)
7+
public abstract class MixinClass {
8+
9+
@Json.Property("among us")
10+
private String st;
11+
12+
@Json.Ignore private String av;
13+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package io.avaje.jsonb.generator.models.valid;
2+
3+
public class MixinTarget {
4+
5+
private String st;
6+
private String av;
7+
8+
public String getSt() {
9+
return st;
10+
}
11+
12+
public void setSt(String st) {
13+
this.st = st;
14+
}
15+
16+
public String getAv() {
17+
return av;
18+
}
19+
20+
public void setAv(String av) {
21+
this.av = av;
22+
}
23+
}

jsonb/src/main/java/io/avaje/jsonb/Json.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
package io.avaje.jsonb;
22

3+
import static java.lang.annotation.RetentionPolicy.CLASS;
4+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
5+
36
import java.lang.annotation.ElementType;
47
import java.lang.annotation.Repeatable;
58
import java.lang.annotation.Retention;
69
import java.lang.annotation.Target;
710

8-
import static java.lang.annotation.RetentionPolicy.CLASS;
9-
import static java.lang.annotation.RetentionPolicy.RUNTIME;
10-
1111
/**
1212
* Marks a type for JSON support.
1313
*
@@ -225,6 +225,14 @@
225225

226226
}
227227

228+
/** Marks a Class as a MixIn class. */
229+
@Retention(CLASS)
230+
@Target({ElementType.TYPE})
231+
@interface MixIn {
232+
/** The concrete type to mix. */
233+
Class<?> value();
234+
}
235+
228236
/**
229237
* The naming convention that we can use for a given type.
230238
*/

0 commit comments

Comments
 (0)