Skip to content

Commit 2c3960d

Browse files
authored
71 warn rich arr map type (#148)
* Use MapTypeInfo to represent map
1 parent ce6f3c8 commit 2c3960d

28 files changed

Lines changed: 286 additions & 212 deletions

annotation/src/main/java/online/sharedtype/SharedType.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,12 +107,14 @@
107107
* Optionals can be marked by either annotations or an optional types, both can be configured via global properties.
108108
* Optional types have a more complicated mapping, where only the outermost optional is recognized as the optional marker,
109109
* any nested optional types are flattened, similar to Jackson's serialization behavior.
110-
* E.g. a type {@code Optional<Optional<List<Optional<String>>>} can be emitted as:
110+
* E.g. a type {@code Optional<Optional<List<String>>} can be emitted as:
111111
* <ul>
112-
* <li>Typescript: {@code String[] | undefined}</li>
112+
* <li>Typescript: {@code string[] | undefined}</li>
113113
* <li>Go: pointers</li>
114114
* <li>Rust: {@code Option<Vec<String>>}</li>
115115
* </ul>
116+
* Optionals inside collection are dropped, as not practically necessary.
117+
* Emitted format can be configured via annotation or global properties.
116118
*
117119
* <p>
118120
* <b>Maps:</b><br>

doc/Development.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Internal types also have javadoc for more information.
1313
* `java8` contains major types for tests.
1414
* `java17` uses symlink to reuse types in `java8` then does more type checks, e.g. for Java `record`.
1515
* `client-test` contains target languages' tests respectively against generated code.
16+
* `e2e` contains e2e json 2-way serialization and deserialization tests against target languages' http servers.
1617

1718
Domain types are shared among processor and integration tests to reduce maven module count.
1819

@@ -27,7 +28,7 @@ Optionally mount tmpfs to save your disk by:
2728
```bash
2829
./mount-tmpfs.sh
2930
```
30-
Optionally setup `MVND_HOME` to use [maven daemon](https://github.com/apache/maven-mvnd) by `./mvnd`
31+
Optionally setup `MVND_HOME` to use [maven daemon](https://github.com/apache/maven-mvnd) by `mvnd`
3132

3233
Choose maven profiles in IDE accordingly as below.
3334

internal/src/main/java/online/sharedtype/processor/domain/def/ClassDef.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,6 @@ public Set<ConcreteTypeInfo> typeInfoSet() {
7474
return typeInfoSet;
7575
}
7676

77-
public boolean isMapType() {
78-
return typeInfoSet.stream().anyMatch(t -> t.getKind() == ConcreteTypeInfo.Kind.MAP);
79-
}
80-
8177
public void addSubtype(TypeDef subtype) {
8278
subtypes.add(subtype);
8379
}
@@ -91,6 +87,11 @@ public void linkTypeInfo(ConcreteTypeInfo typeInfo) {
9187
typeInfoSet.add(typeInfo);
9288
}
9389

90+
/**
91+
* Replace any type variables with the type arguments. The supplied type arguments can also be type variables.
92+
* @param typeArgs type arguments that must have the same size as type variables of this classDef
93+
* @see TypeVariableInfo
94+
*/
9495
public ClassDef reify(List<? extends TypeInfo> typeArgs) {
9596
int l;
9697
if ((l = typeArgs.size()) != typeVariables.size()) {

internal/src/main/java/online/sharedtype/processor/domain/type/ConcreteTypeInfo.java

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,7 @@ public final class ConcreteTypeInfo extends ReferableTypeInfo implements Mappabl
3838
private final List<TypeInfo> typeArgs = Collections.emptyList();
3939

4040
@Getter @Builder.Default
41-
private final Kind kind = Kind.OTHER;
42-
43-
/**
44-
* If this type is defined in global config as base Map type
45-
*/
46-
@Getter
47-
private final boolean baseMapType;
41+
private final Kind kind = Kind.CLASS;
4842

4943
@Builder.Default
5044
private boolean resolved = true;
@@ -135,6 +129,6 @@ public String toString() {
135129
* Date/Time is represented by DateTimeTypeInfo.
136130
*/
137131
public enum Kind {
138-
ENUM, MAP, OTHER
132+
ENUM, CLASS
139133
}
140134
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package online.sharedtype.processor.domain.type;
2+
3+
import lombok.Builder;
4+
import lombok.EqualsAndHashCode;
5+
import lombok.Getter;
6+
import lombok.experimental.Accessors;
7+
8+
import java.util.Map;
9+
10+
@Accessors(fluent = true, chain = false)
11+
@Getter
12+
@EqualsAndHashCode(of = {"keyType", "valueType"}, callSuper = false)
13+
@Builder(toBuilder = true)
14+
public final class MapTypeInfo extends ReferableTypeInfo {
15+
private static final long serialVersionUID = -488629168782059977L;
16+
private final String baseMapTypeQualifiedName;
17+
private final String qualifiedName;
18+
private final TypeInfo keyType;
19+
private final TypeInfo valueType;
20+
21+
@Override
22+
public boolean resolved() {
23+
return keyType.resolved() && valueType.resolved();
24+
}
25+
@Override
26+
public TypeInfo reify(Map<TypeVariableInfo, TypeInfo> mappings) {
27+
TypeInfo reifiedKeyType = keyType.reify(mappings);
28+
TypeInfo reifiedValueType = valueType.reify(mappings);
29+
return reifiedKeyType == keyType && reifiedValueType == valueType ? this : this.toBuilder().keyType(reifiedKeyType).valueType(reifiedValueType).build();
30+
}
31+
32+
@Override
33+
public String toString() {
34+
return String.format("%s[%s->%s]", qualifiedName, keyType, valueType);
35+
}
36+
}

it/java17/src/test/java/online/sharedtype/it/MapClassIntegrationTest.java

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import online.sharedtype.processor.domain.def.ClassDef;
44
import online.sharedtype.processor.domain.type.ConcreteTypeInfo;
55
import online.sharedtype.processor.domain.Constants;
6+
import online.sharedtype.processor.domain.type.MapTypeInfo;
67
import org.junit.jupiter.api.Test;
78

89
import static online.sharedtype.it.support.TypeDefDeserializer.deserializeTypeDef;
@@ -15,49 +16,41 @@ void mapClass() {
1516
assertThat(classDef.components()).satisfiesExactly(
1617
mapField -> {
1718
assertThat(mapField.name()).isEqualTo("mapField");
18-
ConcreteTypeInfo typeInfo = (ConcreteTypeInfo)mapField.type();
19-
assertThat(typeInfo.getKind()).isEqualTo(ConcreteTypeInfo.Kind.MAP);
19+
MapTypeInfo typeInfo = (MapTypeInfo)mapField.type();
2020
assertThat(typeInfo.qualifiedName()).isEqualTo("java.util.concurrent.ConcurrentMap");
21-
assertThat(typeInfo.typeArgs()).hasSize(2).satisfiesExactly(
22-
keyType -> assertThat(keyType).isEqualTo(Constants.BOXED_INT_TYPE_INFO),
23-
valueType -> assertThat(valueType).isEqualTo(Constants.STRING_TYPE_INFO)
24-
);
21+
assertThat(typeInfo.keyType()).isEqualTo(Constants.BOXED_INT_TYPE_INFO);
22+
assertThat(typeInfo.valueType()).isEqualTo(Constants.STRING_TYPE_INFO);
2523
},
2624
enumKeyMapField -> {
2725
assertThat(enumKeyMapField.name()).isEqualTo("enumKeyMapField");
28-
ConcreteTypeInfo typeInfo = (ConcreteTypeInfo) enumKeyMapField.type();
29-
assertThat(typeInfo.getKind()).isEqualTo(ConcreteTypeInfo.Kind.MAP);
26+
MapTypeInfo typeInfo = (MapTypeInfo) enumKeyMapField.type();
3027
assertThat(typeInfo.qualifiedName()).isEqualTo("java.util.Map");
31-
assertThat(typeInfo.typeArgs()).hasSize(2).satisfiesExactly(
28+
assertThat(typeInfo.keyType()).satisfies(
3229
keyType -> {
3330
ConcreteTypeInfo keyTypeInfo = (ConcreteTypeInfo) keyType;
3431
assertThat(keyTypeInfo.qualifiedName()).isEqualTo("online.sharedtype.it.java8.EnumSize");
35-
},
36-
valueType -> assertThat(valueType).isEqualTo(Constants.STRING_TYPE_INFO)
32+
}
3733
);
34+
assertThat(typeInfo.valueType()).isEqualTo(Constants.STRING_TYPE_INFO);
3835
},
3936
customMapField -> {
4037
assertThat(customMapField.name()).isEqualTo("customMapField");
41-
ConcreteTypeInfo typeInfo = (ConcreteTypeInfo) customMapField.type();
42-
assertThat(typeInfo.getKind()).isEqualTo(ConcreteTypeInfo.Kind.MAP);
38+
MapTypeInfo typeInfo = (MapTypeInfo) customMapField.type();
4339
assertThat(typeInfo.qualifiedName()).isEqualTo("online.sharedtype.it.java8.CustomMap");
44-
assertThat(typeInfo.typeArgs()).hasSize(0);
40+
assertThat(typeInfo.keyType()).isEqualTo(Constants.BOXED_INT_TYPE_INFO);
41+
assertThat(typeInfo.valueType()).isEqualTo(Constants.STRING_TYPE_INFO);
4542
},
4643
nestedMapField -> {
4744
assertThat(nestedMapField.name()).isEqualTo("nestedMapField");
48-
ConcreteTypeInfo typeInfo = (ConcreteTypeInfo) nestedMapField.type();
49-
assertThat(typeInfo.getKind()).isEqualTo(ConcreteTypeInfo.Kind.MAP);
45+
MapTypeInfo typeInfo = (MapTypeInfo) nestedMapField.type();
5046
assertThat(typeInfo.qualifiedName()).isEqualTo("java.util.Map");
51-
assertThat(typeInfo.typeArgs()).hasSize(2).satisfiesExactly(
52-
keyType -> assertThat(keyType).isEqualTo(Constants.STRING_TYPE_INFO),
47+
assertThat(typeInfo.keyType()).isEqualTo(Constants.STRING_TYPE_INFO);
48+
assertThat(typeInfo.valueType()).satisfies(
5349
valueType -> {
54-
ConcreteTypeInfo valueTypeInfo = (ConcreteTypeInfo) valueType;
55-
assertThat(typeInfo.getKind()).isEqualTo(ConcreteTypeInfo.Kind.MAP);
50+
MapTypeInfo valueTypeInfo = (MapTypeInfo) valueType;
5651
assertThat(valueTypeInfo.qualifiedName()).isEqualTo("java.util.Map");
57-
assertThat(valueTypeInfo.typeArgs()).hasSize(2).satisfiesExactly(
58-
nestedKeyType -> assertThat(nestedKeyType).isEqualTo(Constants.STRING_TYPE_INFO),
59-
nestedValueType -> assertThat(nestedValueType).isEqualTo(Constants.BOXED_INT_TYPE_INFO)
60-
);
52+
assertThat(valueTypeInfo.keyType()).isEqualTo(Constants.STRING_TYPE_INFO);
53+
assertThat(valueTypeInfo.valueType()).isEqualTo(Constants.BOXED_INT_TYPE_INFO);
6154
}
6255
);
6356
}

it/java17/src/test/java/online/sharedtype/it/OptionalTypeIntegrationTest.java

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package online.sharedtype.it;
22

3-
import online.sharedtype.processor.domain.type.ArrayTypeInfo;
4-
import online.sharedtype.processor.domain.def.ClassDef;
5-
import online.sharedtype.processor.domain.type.ConcreteTypeInfo;
63
import online.sharedtype.processor.domain.Constants;
4+
import online.sharedtype.processor.domain.def.ClassDef;
5+
import online.sharedtype.processor.domain.type.ArrayTypeInfo;
6+
import online.sharedtype.processor.domain.type.MapTypeInfo;
77
import org.junit.jupiter.api.Test;
88

99
import static online.sharedtype.it.support.TypeDefDeserializer.deserializeTypeDef;
@@ -34,13 +34,10 @@ void optionalMethodClass() {
3434
field4 -> {
3535
assertThat(field4.name()).isEqualTo("mapNestedValueOptional");
3636
assertThat(field4.optional()).isTrue();
37-
ConcreteTypeInfo fieldTypeInfo = (ConcreteTypeInfo) field4.type();
38-
assertThat(fieldTypeInfo.getKind()).isEqualTo(ConcreteTypeInfo.Kind.MAP);
37+
MapTypeInfo fieldTypeInfo = (MapTypeInfo) field4.type();
3938
assertThat(fieldTypeInfo.qualifiedName()).isEqualTo("java.util.Map");
40-
assertThat(fieldTypeInfo.typeArgs()).hasSize(2).satisfiesExactly(
41-
keyType -> assertThat(keyType).isEqualTo(Constants.BOXED_INT_TYPE_INFO),
42-
valueType -> assertThat(valueType).isEqualTo(Constants.STRING_TYPE_INFO)
43-
);
39+
assertThat(fieldTypeInfo.keyType()).isEqualTo(Constants.BOXED_INT_TYPE_INFO);
40+
assertThat(fieldTypeInfo.valueType()).isEqualTo(Constants.STRING_TYPE_INFO);
4441
}
4542
);
4643
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
11
package online.sharedtype.it.java8;
22

3+
import online.sharedtype.SharedType;
4+
35
import java.util.HashMap;
46

57
public final class CustomMap extends HashMap<Integer, String> {
68
private static final long serialVersionUID = 2346546437868922578L;
79
}
10+
11+
// the type is treated as a Map type, it should be ignored with a warning
12+
@SharedType
13+
class CustomMapAnnotated extends HashMap<Integer, String> {
14+
private static final long serialVersionUID = 4576189946614168943L;
15+
16+
private int unfortunatelyIgnored;
17+
}

processor/src/main/java/online/sharedtype/processor/context/Context.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,11 @@ public Map<SharedType.TargetType, List<TagLiteralContainer>> extractTagLiterals(
162162
return tagLiteralsByTargetCodeType;
163163
}
164164

165+
public String getTypeQualifiedName(DeclaredType typeMirror) {
166+
TypeElement element = (TypeElement) types.asElement(typeMirror);
167+
return element.getQualifiedName().toString();
168+
}
169+
165170
public FileObject createSourceOutput(String filename) throws IOException {
166171
return processingEnv.getFiler().createResource(StandardLocation.SOURCE_OUTPUT, "", filename);
167172
}

processor/src/main/java/online/sharedtype/processor/parser/CompositeTypeDefParser.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,14 @@ public List<TypeDef> parse(TypeElement typeElement) {
3434
ctx.warn(typeElement, "Type '%s' is an array type, which cannot be parsed and emitted as a standalone type.", typeElement.getQualifiedName());
3535
return Collections.emptyList();
3636
}
37+
if (ctx.isMaplike(typeElement.asType())) {
38+
ctx.warn(typeElement, "Type '%s' is a map type, which cannot be parsed and emitted as a standalone type.", typeElement.getQualifiedName());
39+
return Collections.emptyList();
40+
}
3741
if (ctx.isDatetimelike(typeElement.asType())) {
3842
ctx.warn(typeElement, "Type '%s' is a datetime type, which cannot be parsed and emitted as a standalone type.", typeElement.getQualifiedName());
3943
return Collections.emptyList();
4044
}
41-
// TODO: warn for maplikeType
4245

4346
ctx.info("Processing: %s", typeElement.getQualifiedName());
4447
List<TypeDef> typeDefs = new ArrayList<>();

0 commit comments

Comments
 (0)