Skip to content

Commit f9c019a

Browse files
committed
processor: refactor class generation
1 parent b497935 commit f9c019a

File tree

5 files changed

+439
-270
lines changed

5 files changed

+439
-270
lines changed
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package jtamaro.optics.processor;
2+
3+
import java.util.List;
4+
import java.util.SequencedMap;
5+
import java.util.SequencedSet;
6+
import javax.lang.model.type.TypeMirror;
7+
import javax.lang.model.util.Types;
8+
9+
final class ComponentLensesGenerator {
10+
11+
private ComponentLensesGenerator() {
12+
}
13+
14+
/**
15+
* Generate lenses for each record component.
16+
*/
17+
public static List<String> generate(
18+
Types types,
19+
TypeMirror targetRecordType,
20+
SequencedMap<String, TypeMirror> allComponents,
21+
SequencedSet<String> allComponentNames
22+
) {
23+
return allComponents.sequencedEntrySet()
24+
.stream()
25+
.map(e -> lensForComponent(
26+
types,
27+
targetRecordType,
28+
e.getKey(),
29+
e.getValue(),
30+
allComponentNames
31+
))
32+
.toList();
33+
}
34+
35+
/**
36+
* Generate a lens for a given record component.
37+
*/
38+
private static String lensForComponent(
39+
Types types,
40+
TypeMirror sourceType,
41+
String targetName,
42+
TypeMirror targetType,
43+
SequencedSet<String> allComponentNames
44+
) {
45+
final String sourceTypeStr = Utils.formatType(types, sourceType);
46+
final String componentTypeStr = Utils.formatType(types, targetType);
47+
final String overImpl = Utils.newRecordInstanceExpr(
48+
sourceTypeStr,
49+
targetName,
50+
allComponentNames,
51+
accessor -> "lift.apply(" + accessor + ")"
52+
);
53+
return String.format("""
54+
public static final Lens<%1$s, %1$s, %2$s, %2$s> %3$s = new Lens<>() {
55+
@Override
56+
public %1$s over(Function1<%2$s, %2$s> lift, %1$s source) {
57+
return %4$s;
58+
}
59+
60+
@Override
61+
public %2$s view(%1$s source) {
62+
return source.%3$s();
63+
}
64+
};
65+
""", // Lower indentation on purpose!
66+
sourceTypeStr, // S, T
67+
componentTypeStr, // A, B
68+
targetName, // 3: target component name
69+
overImpl // 4: new instance in over
70+
);
71+
}
72+
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package jtamaro.optics.processor;
2+
3+
import java.util.List;
4+
import java.util.SequencedMap;
5+
import java.util.SequencedSet;
6+
import javax.lang.model.type.DeclaredType;
7+
import javax.lang.model.type.TypeMirror;
8+
import javax.lang.model.util.Types;
9+
10+
final class ComponentTraversalsGenerator {
11+
12+
private static final String METHOD_LENS_AT_INDEX = """
13+
private static <T> Lens<Sequence<T>, Sequence<T>, T, T> lensAtIndex(int idx) {
14+
return new Lens<>() {
15+
@Override
16+
public Sequence<T> over(Function1<T, T> lift, Sequence<T> source) {
17+
return source.zipWithIndex()
18+
.map(p -> p.second() == idx
19+
? lift.apply(p.first())
20+
: p.first());
21+
}
22+
23+
@Override
24+
public T view(Sequence<T> source) {
25+
return source.drop(idx).first();
26+
}
27+
};
28+
}
29+
"""; // Lower indentation on purpose!
30+
31+
private ComponentTraversalsGenerator() {
32+
}
33+
34+
/**
35+
* Generate a traversal that allows to focus on a lens for each element of a
36+
* record component of type list.
37+
*/
38+
public static List<String> generate(
39+
Types types,
40+
TypeMirror targetRecordType,
41+
SequencedMap<String, TypeMirror> allComponents,
42+
SequencedSet<String> allComponentNames
43+
) {
44+
return allComponents.sequencedEntrySet()
45+
.stream()
46+
.map(e -> traversalForComponent(
47+
types,
48+
targetRecordType,
49+
e.getKey(),
50+
getFirstTypeArgument(e.getValue()),
51+
allComponentNames
52+
))
53+
.toList();
54+
}
55+
56+
/**
57+
* Generate a traversal that allows to focus on a lens for a single element of
58+
* a record component of type Sequence.
59+
*/
60+
private static String traversalForComponent(
61+
Types types,
62+
TypeMirror sourceType,
63+
String targetName,
64+
TypeMirror targetElementType,
65+
SequencedSet<String> allComponentNames
66+
) {
67+
final String sourceTypeStr = Utils.formatType(types, sourceType);
68+
final String componentElementTypeStr = Utils.formatType(types, targetElementType);
69+
final String overImpl = Utils.newRecordInstanceExpr(
70+
sourceTypeStr,
71+
targetName,
72+
allComponentNames,
73+
ignored -> "newValue"
74+
);
75+
return String.format("""
76+
public static final Traversal<%1$s, %1$s, Lens<%1$s, %1$s, %2$s, %2$s>, %2$s> %3$sElements = new Traversal<>() {
77+
@Override
78+
public %1$s over(Function1<Lens<%1$s, %1$s, %2$s, %2$s>, %2$s> lift,
79+
%1$s source) {
80+
final Sequence<%2$s> newValue = source.%3$s().zipWithIndex()
81+
.map(p -> lift.apply(%3$s.then(lensAtIndex(p.second()))));
82+
return %4$s;
83+
}
84+
85+
@Override
86+
public <R> R foldMap(R neutralElement,
87+
Function2<R, R, R> reducer,
88+
Function1<Lens<%1$s, %1$s, %2$s, %2$s>, R> map,
89+
%1$s source) {
90+
return source.%3$s().zipWithIndex().foldLeft(
91+
neutralElement,
92+
(acc, p) -> reducer.apply(
93+
acc,
94+
map.apply(%3$s.then(lensAtIndex(p.second())))
95+
)
96+
);
97+
}
98+
};
99+
""", // Lower indentation on purpose!
100+
sourceTypeStr, // S, T
101+
componentElementTypeStr, // A, B
102+
targetName, // 3: target component name
103+
overImpl // 4: new instance in over
104+
);
105+
}
106+
107+
/**
108+
* Get the code that implements the method "lensAtIndex(int)".
109+
*/
110+
public static String getMethodLensAtIndexImpl() {
111+
return METHOD_LENS_AT_INDEX;
112+
}
113+
114+
/**
115+
* Get the {@link TypeMirror} of the first type parameter of the given
116+
* {@link TypeMirror}.
117+
*/
118+
private static TypeMirror getFirstTypeArgument(TypeMirror typeMirror) {
119+
if (typeMirror instanceof DeclaredType declaredType) {
120+
final List<? extends TypeMirror> typeArgs = declaredType.getTypeArguments();
121+
if (!typeArgs.isEmpty()) {
122+
return typeArgs.getFirst();
123+
}
124+
}
125+
throw new IllegalArgumentException("Provided type " + typeMirror + " has no type parameter");
126+
}
127+
}

processor/src/main/java/jtamaro/optics/processor/GlassesProcessor.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ private boolean processRecord(TypeElement recordElement) {
6666
types,
6767
recordElement,
6868
genFile,
69-
packageName,
7069
simpleName
7170
);
7271
return true;

0 commit comments

Comments
 (0)