Skip to content

Commit 1fb98c6

Browse files
committed
Use classes rather than lambdas in the Exposer
This is more readable.
1 parent cb6c121 commit 1fb98c6

File tree

3 files changed

+268
-166
lines changed

3 files changed

+268
-166
lines changed

rt4core/src/main/java/uk/co/farowl/vsj4/core/Exposer.java

Lines changed: 150 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,6 @@
2020
import java.util.Set;
2121
import java.util.StringJoiner;
2222
import java.util.TreeSet;
23-
import java.util.function.BiConsumer;
24-
import java.util.function.Consumer;
25-
import java.util.function.Function;
2623

2724
import org.slf4j.Logger;
2825
import org.slf4j.LoggerFactory;
@@ -137,23 +134,32 @@ static Collection<Class<?>> superClasses(Class<?> c) {
137134
* @param meth method annotated
138135
* @throws InterpreterError on duplicates or unsupported types
139136
*/
140-
void addMethodSpec(Method meth, PythonMethod anno)
137+
private void addMethodSpec(Method meth, PythonMethod anno)
141138
throws InterpreterError {
142-
// For clarity, name lambda expressions for the actions
143-
BiConsumer<MethodSpec, Method> addMethod =
144-
// Add method m to spec ms
145-
(MethodSpec ms, Method m) -> {
146-
ms.add(m, anno.primary(), anno.positionalOnly(),
147-
MethodKind.INSTANCE);
148-
};
149-
Function<Spec, MethodSpec> cast =
150-
// Test and cast a found Spec to MethodSpec
151-
spec -> spec instanceof MethodSpec ? (MethodSpec)spec
152-
: null;
153-
// Now use the generic create/update
154-
addSpec(meth, anno.value(), cast,
155-
(String name) -> new MethodSpec(name, kind()),
156-
ms -> methodSpecs.add(ms), addMethod);
139+
// Define custom actions for a PythonMethod
140+
SpecAdder<MethodSpec, Method> adder = new SpecAdder<>() {
141+
142+
@Override
143+
boolean check(Spec spec) {
144+
return spec instanceof MethodSpec;
145+
}
146+
147+
@Override
148+
MethodSpec makeSpec(String name) {
149+
return new MethodSpec(name, kind());
150+
}
151+
152+
@Override
153+
void addMember(Method m, MethodSpec s) {
154+
s.add(m, anno.primary(), anno.positionalOnly(),
155+
MethodKind.INSTANCE);
156+
}
157+
158+
@Override
159+
void addSpec(MethodSpec s) { methodSpecs.add(s); }
160+
};
161+
162+
adder.add(meth, anno.value());
157163
}
158164

159165
/**
@@ -165,74 +171,32 @@ void addMethodSpec(Method meth, PythonMethod anno)
165171
* @param meth method annotated
166172
* @throws InterpreterError on duplicates or unsupported types
167173
*/
168-
void addStaticMethodSpec(Method meth, PythonStaticMethod anno)
169-
throws InterpreterError {
170-
// For clarity, name lambda expressions for the actions
171-
BiConsumer<StaticMethodSpec, Method> addMethod =
172-
// Add method m to spec ms
173-
(StaticMethodSpec ms, Method m) -> {
174-
ms.add(m, true, anno.positionalOnly(),
175-
MethodKind.STATIC);
176-
};
177-
Function<Spec, StaticMethodSpec> cast =
178-
// Test and cast a found Spec to StaticMethodSpec
179-
spec -> spec instanceof StaticMethodSpec
180-
? (StaticMethodSpec)spec : null;
181-
// Now use the generic create/update
182-
addSpec(meth, anno.value(), cast,
183-
(String name) -> new StaticMethodSpec(name, kind()),
184-
ms -> methodSpecs.add(ms), addMethod);
185-
}
174+
private void addStaticMethodSpec(Method meth,
175+
PythonStaticMethod anno) throws InterpreterError {
176+
// Define custom actions for a PythonStaticMethod
177+
SpecAdder<StaticMethodSpec, Method> adder = new SpecAdder<>() {
178+
179+
@Override
180+
boolean check(Spec spec) {
181+
return spec instanceof StaticMethodSpec;
182+
}
186183

187-
/**
188-
* A helper that avoids repeating nearly the same code for adding
189-
* each particular sub-class of {@link Spec} when a method is
190-
* encountered. The implementation finds or creates a {@code Spec}
191-
* by the given name or method name. It then adds this {@code Spec}
192-
* to {@link #specs}. The caller provides a factory method, in case
193-
* a new {@code Spec} is needed, a method for adding the Spec to a
194-
* type-specific list, and a method for adding the method to the
195-
* {@code Spec}.
196-
*
197-
* @param <MS> the type of {@link Spec} being added or added to.
198-
* @param m the method being adding to the {@code MS}
199-
* @param name specified in the annotation or {@code null}
200-
* @param cast to the {@code MS} if possible or {@code null}
201-
* @param makeSpec constructor for an {@code MS}
202-
* @param addSpec function to add the {@code MS} to the proper list
203-
* @param addMethod function to update the {@code MS} with a method
204-
*/
205-
<MS extends BaseMethodSpec> void addSpec(Method m, String name,
206-
Function<Spec, MS> cast, //
207-
Function<String, MS> makeSpec, //
208-
Consumer<MS> addSpec, //
209-
BiConsumer<MS, Method> addMethod) {
210-
211-
// The name is as annotated or the "natural" one
212-
if (name == null || name.length() == 0)
213-
name = m.getName();
214-
215-
// Find any existing definition
216-
Spec spec = specs.get(name);
217-
MS entry;
218-
if (spec == null) {
219-
// A new entry is needed
220-
entry = makeSpec.apply(name);
221-
specs.put(entry.name, entry);
222-
addSpec.accept(entry);
223-
addMethod.accept(entry, m);
224-
} else if ((entry = cast.apply(spec)) != null) {
225-
// Existing entry will be updated
226-
addMethod.accept(entry, m);
227-
} else {
228-
/*
229-
* Existing entry is not compatible, but make a loose entry
230-
* on which to base the error message.
231-
*/
232-
entry = makeSpec.apply(name);
233-
addMethod.accept(entry, m);
234-
throw duplicateError(name, m, entry, spec);
235-
}
184+
@Override
185+
StaticMethodSpec makeSpec(String name) {
186+
return new StaticMethodSpec(name, kind());
187+
}
188+
189+
@Override
190+
void addMember(Method m, StaticMethodSpec s) {
191+
s.add(m, true, anno.positionalOnly(),
192+
MethodKind.STATIC);
193+
}
194+
195+
@Override
196+
void addSpec(StaticMethodSpec s) { methodSpecs.add(s); }
197+
};
198+
199+
adder.add(meth, anno.value());
236200
}
237201

238202
/**
@@ -1295,6 +1259,106 @@ Class<? extends Annotation> annoClass() {
12951259
}
12961260
}
12971261

1262+
/**
1263+
* Generic code to create a specification of an attribute or method
1264+
* to the tables of the exposer, expressed as an inner class, or to
1265+
* find an existing one and update it.
1266+
* <p>
1267+
* {@link SpecAdder#add(Member, String)} finds a {@code Spec} by the
1268+
* given name in {@link #specs}, or creates one by calling
1269+
* {@link SpecAdder#makeSpec(String) makeSpec} and adds it. If it
1270+
* found an existing specification, it checks the concrete type with
1271+
* {@link SpecAdder#check(Spec) check}. Then it updates it with the
1272+
* new member by calling {@link SpecAdder#addMember(Member, Spec)
1273+
* addMember} and adds it to the type-specific table through a call
1274+
* to {@link SpecAdder#addSpec(Spec) addSpec}.
1275+
* <p>
1276+
* {@link SpecAdder#add(Member, String) add} is provided, but the
1277+
* other four methods must be defined by the caller. We use an
1278+
* anonymous class at the call site to do this customisation.
1279+
*
1280+
* @param <S> type of specification that should be created
1281+
* @param <M> Java member type being described
1282+
*/
1283+
abstract class SpecAdder<S extends Spec, M extends Member> {
1284+
1285+
/**
1286+
* Check a {@code Spec} found by lookup is of actual type
1287+
* {@code S}.
1288+
*
1289+
* @param spec to check
1290+
* @return {@code instanceof S}
1291+
*/
1292+
abstract boolean check(Spec spec);
1293+
1294+
/**
1295+
* Create a new specification of type {@code S}.
1296+
*
1297+
* @param name of the attribute/method.
1298+
* @return a new S
1299+
*/
1300+
abstract S makeSpec(String name);
1301+
1302+
/**
1303+
* Add a definition to an existing specification {@code S s}
1304+
* created or found by name. Generally, the implementation of
1305+
* this references attributes from the annotation encountered.
1306+
*
1307+
* @param m member annotated
1308+
* @param s specification to add
1309+
*/
1310+
abstract void addMember(M m, S s);
1311+
1312+
/**
1313+
* Add a specification of actual type {@code S} to a specific
1314+
* matching collection.
1315+
*
1316+
* @param s new specification
1317+
*/
1318+
abstract void addSpec(S s);
1319+
1320+
/**
1321+
* Process Java member of a Python type or module defined in
1322+
* Java, into a specification for an attribute or method, and
1323+
* add it to the tables of specifications by name. Optionally
1324+
* override the Java name, or leave {@code name} null or empty
1325+
* to accept the Java one.
1326+
*
1327+
* @param name to replace the Java one (or {@code null}/"")
1328+
* @param m member annotated
1329+
* @throws InterpreterError on duplicates or unsupported types
1330+
*/
1331+
@SuppressWarnings("unchecked")
1332+
void add(M m, String name) {
1333+
1334+
// The name is as annotated or the "natural" one
1335+
if (name == null || name.length() == 0)
1336+
name = m.getName();
1337+
1338+
// Find/create and update a specification of 'name'
1339+
Spec spec;
1340+
S entry;
1341+
if ((spec = specs.get(name)) == null) {
1342+
// A new entry is needed
1343+
entry = makeSpec(name);
1344+
specs.put(entry.name, entry);
1345+
addSpec(entry);
1346+
addMember(m, entry);
1347+
} else if (check(spec)) {
1348+
// Existing entry will be updated (cast is safe)
1349+
addMember(m, (S)spec);
1350+
} else {
1351+
/*
1352+
* Existing entry is not compatible, but make a loose
1353+
* entry on which to base the error message.
1354+
*/
1355+
entry = makeSpec(name);
1356+
addMember(m, entry);
1357+
throw duplicateError(name, m, entry, spec);
1358+
}
1359+
}
1360+
}
1361+
12981362
// Plumbing ------------------------------------------------------
12991363

13001364
/**

rt4core/src/main/java/uk/co/farowl/vsj4/core/Interpreter.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ public void addModule(PyModule m) {
5454
* (effectively the source of {@code sys.modules}).
5555
*
5656
* @param name to retrieve
57+
* @return module named
5758
*/
5859
public PyModule getModule(String name) {
5960
Object o = modules.get(name);

0 commit comments

Comments
 (0)