2020import java .util .Set ;
2121import java .util .StringJoiner ;
2222import java .util .TreeSet ;
23- import java .util .function .BiConsumer ;
24- import java .util .function .Consumer ;
25- import java .util .function .Function ;
2623
2724import org .slf4j .Logger ;
2825import 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 /**
0 commit comments