001/*******************************************************************************
002 * Copyright (c) 2017, 2018 Red Hat Inc
003 * All rights reserved. This program and the accompanying materials
004 * are made available under the terms of the Eclipse Public License v1.0
005 * which accompanies this distribution, and is available at
006 * http://www.eclipse.org/legal/epl-v10.html
007 *
008 * Contributors:
009 *     Jens Reimann - initial API and implementation
010 *******************************************************************************/
011package de.dentrassi.varlink.generator;
012
013import static de.dentrassi.varlink.generator.util.JdtHelper.addSimpleAnnotation;
014import static de.dentrassi.varlink.generator.util.JdtHelper.copyNode;
015import static de.dentrassi.varlink.generator.util.JdtHelper.createCompilationUnit;
016import static de.dentrassi.varlink.generator.util.JdtHelper.createField;
017import static de.dentrassi.varlink.generator.util.JdtHelper.createGetter;
018import static de.dentrassi.varlink.generator.util.JdtHelper.createParameter;
019import static de.dentrassi.varlink.generator.util.JdtHelper.createThisAssignment;
020import static de.dentrassi.varlink.generator.util.JdtHelper.make;
021import static de.dentrassi.varlink.generator.util.JdtHelper.newStringLiteral;
022import static de.dentrassi.varlink.generator.util.Names.toLowerFirst;
023import static de.dentrassi.varlink.generator.util.Names.toUpperFirst;
024import static org.eclipse.jdt.core.dom.Modifier.ModifierKeyword.DEFAULT_KEYWORD;
025import static org.eclipse.jdt.core.dom.Modifier.ModifierKeyword.FINAL_KEYWORD;
026import static org.eclipse.jdt.core.dom.Modifier.ModifierKeyword.PRIVATE_KEYWORD;
027import static org.eclipse.jdt.core.dom.Modifier.ModifierKeyword.PROTECTED_KEYWORD;
028import static org.eclipse.jdt.core.dom.Modifier.ModifierKeyword.PUBLIC_KEYWORD;
029import static org.eclipse.jdt.core.dom.Modifier.ModifierKeyword.STATIC_KEYWORD;
030
031import java.util.Arrays;
032import java.util.LinkedHashMap;
033import java.util.LinkedList;
034import java.util.List;
035import java.util.Map;
036import java.util.Objects;
037import java.util.function.BiConsumer;
038import java.util.stream.Stream;
039
040import org.eclipse.emf.common.util.EList;
041import org.eclipse.jdt.core.dom.AST;
042import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
043import org.eclipse.jdt.core.dom.Assignment;
044import org.eclipse.jdt.core.dom.Block;
045import org.eclipse.jdt.core.dom.ClassInstanceCreation;
046import org.eclipse.jdt.core.dom.CompilationUnit;
047import org.eclipse.jdt.core.dom.EnumConstantDeclaration;
048import org.eclipse.jdt.core.dom.EnumDeclaration;
049import org.eclipse.jdt.core.dom.ExpressionMethodReference;
050import org.eclipse.jdt.core.dom.FieldAccess;
051import org.eclipse.jdt.core.dom.FieldDeclaration;
052import org.eclipse.jdt.core.dom.LambdaExpression;
053import org.eclipse.jdt.core.dom.MemberValuePair;
054import org.eclipse.jdt.core.dom.MethodDeclaration;
055import org.eclipse.jdt.core.dom.MethodInvocation;
056import org.eclipse.jdt.core.dom.NormalAnnotation;
057import org.eclipse.jdt.core.dom.ParameterizedType;
058import org.eclipse.jdt.core.dom.PrimitiveType;
059import org.eclipse.jdt.core.dom.ReturnStatement;
060import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
061import org.eclipse.jdt.core.dom.SwitchCase;
062import org.eclipse.jdt.core.dom.SwitchStatement;
063import org.eclipse.jdt.core.dom.Type;
064import org.eclipse.jdt.core.dom.TypeDeclaration;
065import org.eclipse.jdt.core.dom.TypeLiteral;
066import org.eclipse.jdt.core.dom.TypeParameter;
067import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
068import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
069
070import de.dentrassi.varlink.generator.util.JdtHelper;
071import de.dentrassi.varlink.idl.varlinkIdl.Array;
072import de.dentrassi.varlink.idl.varlinkIdl.BasicType;
073import de.dentrassi.varlink.idl.varlinkIdl.Dictionary;
074import de.dentrassi.varlink.idl.varlinkIdl.ElementType;
075import de.dentrassi.varlink.idl.varlinkIdl.Error;
076import de.dentrassi.varlink.idl.varlinkIdl.Field;
077import de.dentrassi.varlink.idl.varlinkIdl.Interface;
078import de.dentrassi.varlink.idl.varlinkIdl.Method;
079import de.dentrassi.varlink.idl.varlinkIdl.Object;
080import de.dentrassi.varlink.idl.varlinkIdl.Optional;
081import de.dentrassi.varlink.idl.varlinkIdl.TypeAlias;
082import de.dentrassi.varlink.idl.varlinkIdl.TypeAliasDefinition;
083import de.dentrassi.varlink.idl.varlinkIdl.TypeReference;
084
085public class JdtGenerator implements Generator {
086
087    private static final String TYPE_TOKEN_TYPE_NAME = "com.google.gson.reflect.TypeToken";
088    private final Options options;
089
090    public JdtGenerator(final Generator.Options options) {
091        Objects.requireNonNull(options);
092
093        this.options = new Options(options);
094        this.options.validate();
095    }
096
097    private static String internalMethodName(final String name) {
098        return "internal" + toUpperFirst(name);
099    }
100
101    @Override
102    public void generate(final Interface iface) {
103
104        final LinkedList<String> toks = new LinkedList<>(Arrays.asList(iface.getName().split("\\.")));
105
106        final String name = toUpperFirst(toks.getLast());
107        final String packageName = iface.getName();
108
109        createCompilationUnit(this.options.getTargetPath(), packageName, name, this.options.getCharacterSet(),
110                (ast, cu) -> {
111
112                    createInterface(ast, cu, iface, name);
113
114                });
115
116        createCompilationUnit(this.options.getTargetPath(), packageName, name + "Impl", this.options.getCharacterSet(),
117                (ast, cu) -> {
118
119                    createImpl(ast, cu, iface, name);
120
121                });
122
123    }
124
125    @SuppressWarnings("unchecked")
126    private void createImpl(final AST ast, final CompilationUnit cu, final Interface iface, final String name) {
127
128        // create type
129
130        final String implName = name + "Impl";
131
132        final TypeDeclaration td = ast.newTypeDeclaration();
133        cu.types().add(td);
134        td.setName(ast.newSimpleName(implName));
135
136        make(td, PUBLIC_KEYWORD);
137
138        final Type parentType = ast.newSimpleType(ast.newName(name));
139        td.superInterfaceTypes().add(parentType);
140
141        // create factory
142
143        createImplFactory(ast, td);
144
145        // create fields
146
147        createField(td, "de.dentrassi.varlink.spi.Connection", "connection", PRIVATE_KEYWORD, FINAL_KEYWORD);
148        createField(td, "de.dentrassi.varlink.internal.VarlinkImpl", "varlink", PRIVATE_KEYWORD, FINAL_KEYWORD);
149
150        // create constructor
151
152        final MethodDeclaration ctor = ast.newMethodDeclaration();
153        td.bodyDeclarations().add(ctor);
154
155        ctor.setConstructor(true);
156        ctor.setName(ast.newSimpleName(implName));
157        make(ctor, PRIVATE_KEYWORD);
158
159        createParameter(ctor, "de.dentrassi.varlink.spi.Connection", "connection", FINAL_KEYWORD);
160        createParameter(ctor, "de.dentrassi.varlink.internal.VarlinkImpl", "varlink", FINAL_KEYWORD);
161
162        // constructor body
163        {
164            final Block body = ast.newBlock();
165            ctor.setBody(body);
166
167            createThisAssignment(body, "connection");
168            createThisAssignment(body, "varlink");
169        }
170
171        // error mapper
172
173        {
174            final MethodDeclaration md = ast.newMethodDeclaration();
175            make(md, PUBLIC_KEYWORD);
176            td.bodyDeclarations().add(md);
177
178            md.setName(ast.newSimpleName("checkError"));
179            createParameter(md, "de.dentrassi.varlink.spi.CallResponse", "response", FINAL_KEYWORD);
180
181            final Block body = ast.newBlock();
182            md.setBody(body);
183
184            final MethodInvocation mi = ast.newMethodInvocation();
185            mi.setExpression(ast.newName("de.dentrassi.varlink.spi.Errors"));
186            mi.setName(ast.newSimpleName("checkErrors"));
187            mi.arguments().add(ast.newSimpleName("response"));
188
189            final ExpressionMethodReference ref = ast.newExpressionMethodReference();
190            ref.setExpression(ast.newThisExpression());
191            ref.setName(ast.newSimpleName("mapError"));
192            mi.arguments().add(ref);
193
194            body.statements().add(ast.newExpressionStatement(mi));
195        }
196
197        {
198            final MethodDeclaration md = ast.newMethodDeclaration();
199            make(md, PUBLIC_KEYWORD);
200            td.bodyDeclarations().add(md);
201
202            md.setName(ast.newSimpleName("mapError"));
203            createParameter(md, "java.lang.String", "error", FINAL_KEYWORD);
204            createParameter(md, "de.dentrassi.varlink.spi.CallResponse", "response", FINAL_KEYWORD);
205            md.setReturnType2(ast.newSimpleType(ast.newName("java.lang.RuntimeException")));
206
207            final Block body = ast.newBlock();
208            md.setBody(body);
209
210            final SwitchStatement sw = ast.newSwitchStatement();
211            body.statements().add(sw);
212            sw.setExpression(ast.newSimpleName("error"));
213
214            errors(iface).forEach(error -> {
215                final String errorName = errorTypeName(error);
216                final String fullErrorName = iface.getName() + "." + errorName;
217
218                final SwitchCase sc = ast.newSwitchCase();
219                sc.setExpression(JdtHelper.newStringLiteral(ast, fullErrorName));
220                sw.statements().add(sc);
221
222                final FieldAccess fa = ast.newFieldAccess();
223                fa.setExpression(ast.newThisExpression());
224                fa.setName(ast.newSimpleName("varlink"));
225
226                final MethodInvocation fromJson = ast.newMethodInvocation();
227                fromJson.setExpression(fa);
228                fromJson.setName(ast.newSimpleName("fromJson"));
229
230                // type name
231
232                final TypeLiteral typeLiteral = ast.newTypeLiteral();
233                typeLiteral.setType(ast.newSimpleType(ast.newName(errorName + ".Parameters")));
234
235                fromJson.arguments().add(typeLiteral);
236
237                // parameters
238
239                final MethodInvocation parameters = ast.newMethodInvocation();
240                parameters.setExpression(ast.newSimpleName("response"));
241                parameters.setName(ast.newSimpleName("getParameters"));
242                fromJson.arguments().add(parameters);
243
244                // new exception
245
246                final ClassInstanceCreation cic = ast.newClassInstanceCreation();
247                cic.setType(ast.newSimpleType(ast.newName(errorName)));
248                cic.arguments().add(fromJson);
249
250                // return
251
252                final ReturnStatement ret = ast.newReturnStatement();
253                ret.setExpression(cic);
254                sw.statements().add(ret);
255            });
256
257            {
258                final SwitchCase sc = ast.newSwitchCase();
259                sc.setExpression(null);
260                sw.statements().add(sc);
261                final ReturnStatement ret = ast.newReturnStatement();
262                ret.setExpression(ast.newNullLiteral());
263                sw.statements().add(ret);
264            }
265
266        }
267
268        // async creator
269
270        /*
271         * @Override public Async async() { return new Async() {
272         *
273         * @Override public CompletableFuture<List<Netdev>> list() { return
274         * executeList(); } }; }
275         */
276
277        {
278            final MethodDeclaration md = ast.newMethodDeclaration();
279            td.bodyDeclarations().add(md);
280
281            md.setName(ast.newSimpleName("async"));
282            addSimpleAnnotation(md, "Override");
283            make(md, PUBLIC_KEYWORD);
284
285            md.setReturnType2(ast.newSimpleType(ast.newName("Async")));
286
287            final Block body = ast.newBlock();
288            md.setBody(body);
289
290            // inner class
291
292            final ReturnStatement ret = ast.newReturnStatement();
293            body.statements().add(ret);
294
295            final ClassInstanceCreation cic = ast.newClassInstanceCreation();
296            cic.setType(ast.newSimpleType(ast.newName("Async")));
297            ret.setExpression(cic);
298
299            final AnonymousClassDeclaration acc = ast.newAnonymousClassDeclaration();
300            cic.setAnonymousClassDeclaration(acc);
301
302            forMethods(ast, iface, (m, amd) -> {
303
304                acc.bodyDeclarations().add(amd);
305
306                amd.setName(ast.newSimpleName(m.getName()));
307                make(amd, PUBLIC_KEYWORD);
308                makeAsync(amd);
309
310                final Block asyncBody = ast.newBlock();
311                amd.setBody(asyncBody);
312
313                final ReturnStatement asyncRet = ast.newReturnStatement();
314                asyncBody.statements().add(asyncRet);
315
316                final MethodInvocation mi = ast.newMethodInvocation();
317                mi.setName(ast.newSimpleName(internalMethodName(m.getName())));
318
319                for (final String argName : m.getParameters().keySet()) {
320                    mi.arguments().add(ast.newSimpleName(argName));
321                }
322
323                asyncRet.setExpression(mi);
324            });
325
326        }
327
328        // internal methods
329
330        forMethods(ast, iface, (m, md) -> {
331            make(md, PROTECTED_KEYWORD);
332            td.bodyDeclarations().add(md);
333            md.setName(ast.newSimpleName(internalMethodName(m.getName())));
334            makeAsync(md);
335            createInternalMethod(td, m, md);
336        });
337
338    }
339
340    private String errorTypeName(final Error error) {
341        return error.getName() + "Exception";
342    }
343
344    @SuppressWarnings("unchecked")
345    private void createInternalMethod(final TypeDeclaration parentTypeDeclaration, final MethodInformation m,
346            final MethodDeclaration md) {
347        final AST ast = md.getAST();
348
349        final Block body = ast.newBlock();
350        md.setBody(body);
351
352        /*
353         * return this.connection.call(CallRequest.of("io.systemd.network.List"))
354         * .thenApply(cr -> { check(cr);
355         *
356         * final Iterator<JsonElement> i = cr.getParameters().values().iterator();
357         *
358         * return asList( this.varlink .fromJson( Netdev[].class, i.next())); }); }
359         */
360
361        // add arguments
362
363        if (!m.getParameters().isEmpty()) {
364
365            // code: Map<String,Object> parameters = new HashMap<> ();
366
367            final VariableDeclarationFragment parameters = ast.newVariableDeclarationFragment();
368            parameters.setName(ast.newSimpleName("parameters"));
369
370            final VariableDeclarationStatement decl = ast.newVariableDeclarationStatement(parameters);
371            body.statements().add(decl);
372            final ParameterizedType map = ast.newParameterizedType(ast.newSimpleType(ast.newName("java.util.Map")));
373            map.typeArguments().add(ast.newSimpleType(ast.newName("java.lang.String")));
374            map.typeArguments().add(ast.newSimpleType(ast.newName("java.lang.Object")));
375
376            decl.setType(map);
377
378            final ClassInstanceCreation init = ast.newClassInstanceCreation();
379            init.setType(ast.newParameterizedType(ast.newSimpleType(ast.newName("java.util.HashMap"))));
380            init.arguments().add(ast.newNumberLiteral(Integer.toString(m.getParameters().size())));
381            parameters.setInitializer(init);
382
383            for (final String argName : m.getParameters().keySet()) {
384                final MethodInvocation mi = ast.newMethodInvocation();
385                mi.setExpression(ast.newSimpleName("parameters"));
386                mi.setName(ast.newSimpleName("put"));
387
388                mi.arguments().add(JdtHelper.newStringLiteral(ast, argName));
389                mi.arguments().add(ast.newSimpleName(argName));
390            }
391
392        }
393
394        // return
395
396        final ReturnStatement ret = ast.newReturnStatement();
397        body.statements().add(ret);
398
399        final MethodInvocation mi = ast.newMethodInvocation();
400        mi.setName(ast.newSimpleName("call"));
401        final FieldAccess fa = ast.newFieldAccess();
402        fa.setExpression(ast.newThisExpression());
403        fa.setName(ast.newSimpleName("connection"));
404        mi.setExpression(fa);
405
406        final MethodInvocation cr = ast.newMethodInvocation();
407        cr.setExpression(ast.newName("de.dentrassi.varlink.spi.CallRequest"));
408        cr.setName(ast.newSimpleName("of"));
409        cr.arguments().add(newStringLiteral(ast, m.getInterface().getName() + "." + toUpperFirst(m.getName())));
410
411        if (!m.getParameters().isEmpty()) {
412            cr.arguments().add(ast.newSimpleName("parameters"));
413        }
414
415        mi.arguments().add(cr);
416
417        final MethodInvocation thenApply = ast.newMethodInvocation();
418        thenApply.setName(ast.newSimpleName("thenApply"));
419        thenApply.setExpression(mi);
420
421        // add transformation
422
423        final LambdaExpression le = ast.newLambdaExpression();
424        le.setParentheses(false);
425        thenApply.arguments().add(le);
426        final VariableDeclarationFragment p = ast.newVariableDeclarationFragment();
427        p.setName(ast.newSimpleName("result"));
428        le.parameters().add(p);
429        final Block transform = ast.newBlock();
430        le.setBody(transform);
431
432        {
433            // check result
434
435            final MethodInvocation check = ast.newMethodInvocation();
436            check.setName(ast.newSimpleName("checkError"));
437            transform.statements().add(ast.newExpressionStatement(check));
438            check.arguments().add(ast.newSimpleName("result"));
439        }
440
441        if (m.getReturnTypes().isEmpty()) {
442
443            final ReturnStatement transformRet = ast.newReturnStatement();
444            transformRet.setExpression(ast.newNullLiteral());
445            transform.statements().add(transformRet);
446
447        } else {
448
449            final int returns = m.getReturnTypes().size();
450
451            if (returns > 0) {
452
453                // return this.varlink.fromJson(DriveCondition.class, result.getParameters());
454                // return this.varlink.fromJson(DriveCondition.class,
455                // result.getFirstParameter());
456
457                final FieldAccess varlink = ast.newFieldAccess();
458                varlink.setExpression(ast.newThisExpression());
459                varlink.setName(ast.newSimpleName("varlink"));
460
461                final MethodInvocation fromJson = ast.newMethodInvocation();
462                fromJson.setExpression(varlink);
463                fromJson.setName(ast.newSimpleName("fromJson"));
464
465                // FIXME: add to parent
466                {
467                    final ParameterizedType ttt = ast.newParameterizedType(
468                            ast.newSimpleType(ast.newName(TYPE_TOKEN_TYPE_NAME)));
469
470                    ttt.typeArguments().add(m.createMainReturnType(ast));
471                    final ClassInstanceCreation tt = ast.newClassInstanceCreation();
472                    tt.setType(JdtHelper.copyNode(ast, ttt));
473
474                    final AnonymousClassDeclaration decl = ast.newAnonymousClassDeclaration();
475                    tt.setAnonymousClassDeclaration(decl);
476
477                    final MethodInvocation getType = ast.newMethodInvocation();
478                    getType.setExpression(tt);
479                    getType.setName(ast.newSimpleName("getType"));
480
481                    final VariableDeclarationFragment vdf = ast.newVariableDeclarationFragment();
482                    vdf.setName(ast.newSimpleName(m.getName() + "_returnTypeToken"));
483                    vdf.setInitializer(getType);
484                    final FieldDeclaration fd = ast.newFieldDeclaration(vdf);
485                    fd.setType(ast.newSimpleType(ast.newName("java.lang.reflect.Type")));
486                    make(fd, PRIVATE_KEYWORD, FINAL_KEYWORD, STATIC_KEYWORD);
487
488                    parentTypeDeclaration.bodyDeclarations().add(fd);
489                }
490
491                fromJson.arguments().add(ast.newSimpleName(m.getName() + "_returnTypeToken"));
492
493                // json fragment
494
495                final MethodInvocation fragment = ast.newMethodInvocation();
496                if (returns == 1) {
497                    fragment.setName(ast.newSimpleName("getFirstParameter"));
498                } else {
499                    fragment.setName(ast.newSimpleName("getParameters"));
500                }
501                fragment.setExpression(ast.newSimpleName("result"));
502
503                fromJson.arguments().add(fragment);
504
505                // return
506
507                final ReturnStatement transformRet = ast.newReturnStatement();
508                transformRet.setExpression(fromJson);
509                transform.statements().add(transformRet);
510            }
511
512            // FIXME: handle return type
513
514            // FIXME: handle n
515
516        }
517
518        // set return
519
520        ret.setExpression(thenApply);
521    }
522
523    @SuppressWarnings("unchecked")
524    private void createImplFactory(final AST ast, final TypeDeclaration td) {
525
526        final TypeDeclaration ftd = ast.newTypeDeclaration();
527        td.bodyDeclarations().add(ftd);
528        ftd.setName(ast.newSimpleName("Factory"));
529        make(ftd, PUBLIC_KEYWORD, STATIC_KEYWORD);
530
531        ftd.superInterfaceTypes().add(ast.newSimpleType(ast.newName("de.dentrassi.varlink.spi.Factory")));
532
533        /*
534         *
535         * @Override public <T> T create(final VarlinkImpl varlink, final Class<T>
536         * clazz, final Connection connection)
537         */
538
539        final MethodDeclaration md = ast.newMethodDeclaration();
540        ftd.bodyDeclarations().add(md);
541        md.setName(ast.newSimpleName("create"));
542        md.setReturnType2(ast.newSimpleType(ast.newName("T")));
543        make(md, PUBLIC_KEYWORD);
544        addSimpleAnnotation(md, "Override");
545
546        final TypeParameter tp = ast.newTypeParameter();
547        tp.setName(ast.newSimpleName("T"));
548        md.typeParameters().add(tp);
549
550        final ParameterizedType clazz = ast.newParameterizedType(ast.newSimpleType(ast.newName("Class")));
551
552        clazz.typeArguments().add(ast.newSimpleType(ast.newName("T")));
553
554        createParameter(md, "de.dentrassi.varlink.internal.VarlinkImpl", "varlink", FINAL_KEYWORD);
555        createParameter(md, clazz, "clazz", FINAL_KEYWORD);
556        createParameter(md, "de.dentrassi.varlink.spi.Connection", "connection", FINAL_KEYWORD);
557
558        final Block body = ast.newBlock();
559        md.setBody(body);
560
561        // return clazz.cast(new Impl(varlink,connection));
562
563        final ReturnStatement ret = ast.newReturnStatement();
564        body.statements().add(ret);
565
566        final MethodInvocation cast = ast.newMethodInvocation();
567        cast.setName(ast.newSimpleName("cast"));
568        cast.setExpression(ast.newSimpleName("clazz"));
569
570        ret.setExpression(cast);
571
572        final ClassInstanceCreation newImpl = ast.newClassInstanceCreation();
573        newImpl.setType(ast.newSimpleType(ast.newName(td.getName().getIdentifier())));
574        cast.arguments().add(newImpl);
575
576        newImpl.arguments().add(ast.newSimpleName("connection"));
577        newImpl.arguments().add(ast.newSimpleName("varlink"));
578    }
579
580    private static final Stream<Error> errors(final Interface iface) {
581        return iface.getMembers().stream().filter(m -> m instanceof Error).map(m -> (Error) m);
582    }
583
584    private static final Stream<Method> methods(final Interface iface) {
585        return iface.getMembers().stream().filter(m -> m instanceof Method).map(m -> (Method) m);
586    }
587
588    private static final Stream<TypeAlias> types(final Interface iface) {
589        return iface.getMembers().stream().filter(m -> m instanceof TypeAlias).map(m -> (TypeAlias) m);
590    }
591
592    private static final Stream<MethodInformation> map(final Interface iface, final AST ast,
593            final Stream<Method> stream) {
594        return stream.map(m -> map(ast, iface, m));
595    }
596
597    @SuppressWarnings("unchecked")
598    private static final void forMethods(final AST ast, final Interface iface,
599            final BiConsumer<MethodInformation, MethodDeclaration> consumer) {
600
601        map(iface, ast, methods(iface)).forEach(m -> {
602
603            final MethodDeclaration md = ast.newMethodDeclaration();
604            md.setName(ast.newSimpleName(m.getName()));
605
606            md.setReturnType2(m.createMainReturnType(ast));
607
608            for (final Map.Entry<String, Type> parameter : m.getParameters().entrySet()) {
609                final SingleVariableDeclaration par = ast.newSingleVariableDeclaration();
610                par.setName(ast.newSimpleName(parameter.getKey()));
611                par.setType(copyNode(ast, parameter.getValue()));
612                make(par, FINAL_KEYWORD);
613                md.parameters().add(par);
614            }
615
616            consumer.accept(m, md);
617
618        });
619
620    }
621
622    private static MethodInformation map(final AST ast, final Interface iface, final Method method) {
623
624        final String name = toLowerFirst(method.getName());
625
626        final Map<String, Type> parameters = new LinkedHashMap<>();
627        final Map<String, Type> returns = new LinkedHashMap<>();
628
629        final Object arguments = method.getArguments().getArguments();
630        if (arguments != null) {
631            for (final Field field : arguments.getFields()) {
632                final Type type = asType(ast, methodArgumentsParentName(method), field);
633                parameters.put(field.getName(), type);
634            }
635        }
636
637        final Object result = method.getResult().getResult();
638        if (result != null) {
639            for (final Field field : result.getFields()) {
640                final Type type = asType(ast, methodResultParentName(method), field);
641                returns.put(field.getName(), type);
642            }
643        }
644
645        return new MethodInformation(iface, method, name, returns, parameters);
646    }
647
648    private static class MethodInformation {
649        private final Interface iface;
650        private final String name;
651        private final Method method;
652        private final Map<String, Type> returnTypes;
653        private final Map<String, Type> parameters;
654
655        public MethodInformation(final Interface iface, final Method method, final String name,
656                final Map<String, Type> returnTypes, final Map<String, Type> parameters) {
657            this.iface = iface;
658            this.method = method;
659            this.name = name;
660            this.returnTypes = returnTypes;
661            this.parameters = parameters;
662        }
663
664        public Interface getInterface() {
665            return this.iface;
666        }
667
668        public String getName() {
669            return this.name;
670        }
671
672        public Map<String, Type> getParameters() {
673            return this.parameters;
674        }
675
676        public Map<String, Type> getReturnTypes() {
677            return this.returnTypes;
678        }
679
680        public Type createMainReturnType(final AST ast) {
681
682            if (this.returnTypes.isEmpty()) {
683
684                return ast.newPrimitiveType(PrimitiveType.VOID);
685
686            } else if (this.returnTypes.size() == 1) {
687
688                return JdtHelper.copyNode(ast, this.returnTypes.values().iterator().next());
689
690            } else {
691
692                return ast.newSimpleType(ast.newSimpleName(methodResultParentName(this.method)));
693
694            }
695
696        }
697
698    }
699
700    @SuppressWarnings("unchecked")
701    private void createInterface(final AST ast, final CompilationUnit cu, final Interface iface, final String name) {
702
703        final TypeDeclaration td = ast.newTypeDeclaration();
704        cu.types().add(td);
705        td.setInterface(true);
706        td.setName(ast.newSimpleName(name));
707
708        make(td, PUBLIC_KEYWORD);
709
710        final NormalAnnotation ann = JdtHelper.addAnnotation(td, "de.dentrassi.varlink.spi.Interface");
711
712        {
713            final MemberValuePair mvpName = ast.newMemberValuePair();
714            ann.values().add(mvpName);
715            mvpName.setName(ast.newSimpleName("name"));
716            mvpName.setValue(JdtHelper.newStringLiteral(ast, iface.getName()));
717        }
718
719        {
720            final MemberValuePair mvpFactory = ast.newMemberValuePair();
721            ann.values().add(mvpFactory);
722            mvpFactory.setName(ast.newSimpleName("factory"));
723
724            final TypeLiteral fn = ast.newTypeLiteral();
725            fn.setType(ast.newSimpleType(ast.newName(name + "Impl.Factory")));
726
727            mvpFactory.setValue(fn);
728        }
729
730        // create types
731
732        createTypes(td, iface);
733
734        // create errors
735
736        createErrors(td, iface);
737
738        /*
739         *
740         * public interface Async { public CompletableFuture<List<Netdev>> list(); }
741         *
742         * public interface Sync { public List<Netdev> list(); }
743         *
744         * public Async async();
745         *
746         * public default Sync sync() { return new Sync() {
747         *
748         * @Override public List<Netdev> list() { return Syncer.await(async().list()); }
749         * }; }
750         */
751
752        // create async
753
754        {
755            // public interface Async { ... }
756
757            final TypeDeclaration async = ast.newTypeDeclaration();
758            td.bodyDeclarations().add(async);
759            async.setInterface(true);
760
761            make(async, PUBLIC_KEYWORD);
762            async.setName(ast.newSimpleName("Async"));
763
764            forMethods(ast, iface, (m, md) -> {
765                make(md, PUBLIC_KEYWORD);
766                async.bodyDeclarations().add(md);
767                makeAsync(md);
768            });
769
770            // public Async async();
771
772            final MethodDeclaration md = ast.newMethodDeclaration();
773            td.bodyDeclarations().add(md);
774
775            md.setName(ast.newSimpleName("async"));
776            make(md, PUBLIC_KEYWORD);
777
778            final Type rt = ast.newSimpleType(ast.newSimpleName("Async"));
779            md.setReturnType2(rt);
780        }
781
782        // create sync
783
784        {
785            // public interface Sync { ... }
786
787            final TypeDeclaration sync = ast.newTypeDeclaration();
788            td.bodyDeclarations().add(sync);
789            sync.setInterface(true);
790
791            make(sync, PUBLIC_KEYWORD);
792            sync.setName(ast.newSimpleName("Sync"));
793
794            // methods
795
796            forMethods(ast, iface, (m, md) -> {
797                make(md, PUBLIC_KEYWORD);
798                sync.bodyDeclarations().add(md);
799            });
800
801            {
802                final MethodDeclaration smd = ast.newMethodDeclaration();
803                smd.setName(ast.newSimpleName("sync"));
804                make(smd, PUBLIC_KEYWORD, DEFAULT_KEYWORD);
805                td.bodyDeclarations().add(smd);
806
807                final Block body = ast.newBlock();
808                smd.setBody(body);
809
810                final ReturnStatement ret = ast.newReturnStatement();
811                body.statements().add(ret);
812
813                final ClassInstanceCreation cic = ast.newClassInstanceCreation();
814                cic.setType(ast.newSimpleType(ast.newName("Sync")));
815                ret.setExpression(cic);
816                smd.setReturnType2(ast.newSimpleType(ast.newName("Sync")));
817
818                final AnonymousClassDeclaration acc = ast.newAnonymousClassDeclaration();
819                cic.setAnonymousClassDeclaration(acc);
820
821                forMethods(ast, iface, (m, md) -> {
822
823                    make(md, PUBLIC_KEYWORD);
824                    acc.bodyDeclarations().add(md);
825
826                    final Block mbody = ast.newBlock();
827                    md.setBody(mbody);
828
829                    // return Syncer.await(async().list());
830
831                    final MethodInvocation await = ast.newMethodInvocation();
832
833                    await.setExpression(ast.newName("de.dentrassi.varlink.spi.Syncer"));
834                    await.setName(ast.newSimpleName("await"));
835
836                    final MethodInvocation asyncCall = ast.newMethodInvocation();
837                    asyncCall.setName(ast.newSimpleName("async"));
838
839                    final MethodInvocation mcall = ast.newMethodInvocation();
840                    mcall.setName(ast.newSimpleName(m.getName()));
841                    mcall.setExpression(asyncCall);
842
843                    await.arguments().add(mcall);
844
845                    // add arguments
846
847                    for (final String argName : m.getParameters().keySet()) {
848                        mcall.arguments().add(ast.newSimpleName(argName));
849                    }
850
851                    if (m.getReturnTypes().isEmpty()) {
852                        mbody.statements().add(ast.newExpressionStatement(await));
853                    } else {
854                        final ReturnStatement rs = ast.newReturnStatement();
855                        rs.setExpression(await);
856                        mbody.statements().add(rs);
857                    }
858
859                });
860            }
861
862        }
863
864    }
865
866    private static String methodArgumentsParentName(final Method method) {
867        return method.getName() + "_In";
868    }
869
870    private static String methodResultParentName(final Method method) {
871        return method.getName() + "_Out";
872    }
873
874    private void createErrors(final TypeDeclaration td, final Interface iface) {
875
876        final AST ast = td.getAST();
877        errors(iface).forEach(error -> {
878
879            createError(td, ast, error);
880
881        });
882    }
883
884    @SuppressWarnings("unchecked")
885    private void createError(final TypeDeclaration td, final AST ast, final Error error) {
886        final TypeDeclaration etd = ast.newTypeDeclaration();
887        td.bodyDeclarations().add(etd);
888
889        etd.setName(ast.newSimpleName(errorTypeName(error)));
890        make(etd, PUBLIC_KEYWORD, STATIC_KEYWORD);
891
892        etd.setSuperclassType(ast.newSimpleType(ast.newName("java.lang.RuntimeException")));
893
894        // insert serialVersionUID
895        {
896            createSerialId(ast, etd.bodyDeclarations());
897        }
898
899        createType(etd, "Parameters", error.getProperties());
900
901        // field
902
903        {
904            final VariableDeclarationFragment vdf = ast.newVariableDeclarationFragment();
905            vdf.setName(ast.newSimpleName("parameters"));
906            final FieldDeclaration fd = ast.newFieldDeclaration(vdf);
907            fd.setType(ast.newSimpleType(ast.newSimpleName("Parameters")));
908            make(fd, PRIVATE_KEYWORD, FINAL_KEYWORD);
909            etd.bodyDeclarations().add(fd);
910        }
911
912        // constructor
913
914        {
915            final MethodDeclaration ctor = ast.newMethodDeclaration();
916            ctor.setConstructor(true);
917            ctor.setName(ast.newSimpleName(errorTypeName(error)));
918            make(ctor, PUBLIC_KEYWORD);
919            etd.bodyDeclarations().add(ctor);
920            createParameter(ctor, "Parameters", "parameters", FINAL_KEYWORD);
921
922            final Block body = ast.newBlock();
923            ctor.setBody(body);
924
925            JdtHelper.createThisAssignment(body, "parameters");
926        }
927
928        // getter
929
930        {
931            final MethodDeclaration getter = createGetter(ast, ast.newSimpleType(ast.newSimpleName("Parameters")),
932                    "parameters");
933            etd.bodyDeclarations().add(getter);
934        }
935    }
936
937    @SuppressWarnings({ "unchecked", "rawtypes" })
938    private static void createSerialId(final AST ast, final List body) {
939        // private static final long serialVersionUID = 1L;
940
941        final VariableDeclarationFragment vdf = ast.newVariableDeclarationFragment();
942        vdf.setInitializer(ast.newNumberLiteral("1L"));
943        vdf.setName(ast.newSimpleName("serialVersionUID"));
944        final FieldDeclaration fd = ast.newFieldDeclaration(vdf);
945        fd.setType(ast.newPrimitiveType(PrimitiveType.LONG));
946        make(fd, PRIVATE_KEYWORD, STATIC_KEYWORD, FINAL_KEYWORD);
947
948        body.add(fd);
949    }
950
951    private void createTypes(final TypeDeclaration td, final Interface iface) {
952
953        types(iface).forEach(type -> {
954            createType(td, type.getName(), type.getDefinition());
955        });
956
957        methods(iface).forEach(method -> {
958
959            for (final Field field : method.getArguments().getArguments().getFields()) {
960                methodCreateTopLevelType(td, methodArgumentsParentName(method) + "_" + field.getName(),
961                        field.getType());
962            }
963
964            if (method.getResult().getResult().getFields().size() <= 1) {
965                for (final Field field : method.getResult().getResult().getFields()) {
966                    methodCreateTopLevelType(td, methodResultParentName(method) + "_" + field.getName(),
967                            field.getType());
968                }
969            } else {
970                createType(td, methodResultParentName(method), method.getResult().getResult());
971            }
972
973        });
974    }
975
976    private void methodCreateTopLevelType(final TypeDeclaration td, final String parentName, final ElementType type) {
977        if (type instanceof Object || type instanceof de.dentrassi.varlink.idl.varlinkIdl.Enum) {
978            createType(td, parentName, (Object) type);
979        } else if (type instanceof Array) {
980            methodCreateTopLevelType(td, parentName, ((Array) type).getType());
981        } else if (type instanceof Dictionary) {
982            methodCreateTopLevelType(td, parentName, ((Dictionary) type).getType());
983        } else if (type instanceof Optional) {
984            methodCreateTopLevelType(td, parentName, ((Optional) type).getType());
985        }
986    }
987
988    @SuppressWarnings("unchecked")
989    private void createType(final TypeDeclaration parent, final String parentName, final TypeAliasDefinition type) {
990        final AST ast = parent.getAST();
991
992        if (type instanceof de.dentrassi.varlink.idl.varlinkIdl.Object) {
993
994            final TypeDeclaration td = ast.newTypeDeclaration();
995            parent.bodyDeclarations().add(td);
996
997            td.setName(ast.newSimpleName(toUpperFirst(parentName)));
998            make(td, PUBLIC_KEYWORD, STATIC_KEYWORD);
999
1000            final de.dentrassi.varlink.idl.varlinkIdl.Object o = (de.dentrassi.varlink.idl.varlinkIdl.Object) type;
1001
1002            for (final Field field : o.getFields()) {
1003
1004                final String name = toLowerFirst(field.getName());
1005
1006                // create enum type
1007
1008                if (field.getType() instanceof de.dentrassi.varlink.idl.varlinkIdl.Enum) {
1009                    final de.dentrassi.varlink.idl.varlinkIdl.Enum en = (de.dentrassi.varlink.idl.varlinkIdl.Enum) field
1010                            .getType();
1011                    createEnum(td, toUpperFirst(name), en.getFields());
1012                }
1013
1014                // created nested type
1015                if (field.getType() instanceof Object) {
1016                    createType(td, field.getName(), (Object) field.getType());
1017                }
1018
1019                // create field
1020
1021                {
1022                    final VariableDeclarationFragment vdf = ast.newVariableDeclarationFragment();
1023                    vdf.setName(ast.newSimpleName(name));
1024                    final FieldDeclaration fd = ast.newFieldDeclaration(vdf);
1025                    fd.setType(asType(ast, null, field));
1026                    make(fd, PRIVATE_KEYWORD);
1027
1028                    td.bodyDeclarations().add(fd);
1029                }
1030
1031                // create getter
1032
1033                {
1034                    final MethodDeclaration md = createGetter(ast, asType(ast, null, field), name);
1035                    td.bodyDeclarations().add(md);
1036                }
1037
1038                // create setter
1039
1040                {
1041                    final MethodDeclaration md = ast.newMethodDeclaration();
1042                    md.setName(ast.newSimpleName("set" + toUpperFirst(name)));
1043                    td.bodyDeclarations().add(md);
1044                    make(md, PUBLIC_KEYWORD);
1045
1046                    final SingleVariableDeclaration svd = ast.newSingleVariableDeclaration();
1047                    svd.setName(ast.newSimpleName(name));
1048                    svd.setType(asType(ast, null, field));
1049
1050                    md.parameters().add(svd);
1051
1052                    final Block body = ast.newBlock();
1053                    md.setBody(body);
1054
1055                    final FieldAccess fa = ast.newFieldAccess();
1056                    fa.setExpression(ast.newThisExpression());
1057                    fa.setName(ast.newSimpleName(name));
1058
1059                    final Assignment ass = ast.newAssignment();
1060                    ass.setLeftHandSide(fa);
1061                    ass.setRightHandSide(ast.newSimpleName(name));
1062
1063                    body.statements().add(ast.newExpressionStatement(ass));
1064
1065                }
1066
1067            }
1068
1069        } else if (type instanceof de.dentrassi.varlink.idl.varlinkIdl.Enum) {
1070
1071            // FIXME: create enums
1072        }
1073    }
1074
1075    @SuppressWarnings("unchecked")
1076    private void createEnum(final TypeDeclaration td, final String name, final EList<String> literals) {
1077        final AST ast = td.getAST();
1078
1079        final EnumDeclaration ed = ast.newEnumDeclaration();
1080        td.bodyDeclarations().add(ed);
1081        ed.setName(ast.newSimpleName(name));
1082        make(ed, PUBLIC_KEYWORD, STATIC_KEYWORD);
1083
1084        for (final String literal : literals) {
1085            final EnumConstantDeclaration ecd = ast.newEnumConstantDeclaration();
1086            ecd.setName(ast.newSimpleName(literal));
1087            ed.enumConstants().add(ecd);
1088        }
1089    }
1090
1091    private static Type asType(final AST ast, String parentName, final Field field) {
1092
1093        if (parentName != null) {
1094            parentName = parentName + "_" + field.getName();
1095        } else {
1096            parentName = field.getName();
1097        }
1098
1099        return asType(ast, parentName, field.getType());
1100    }
1101
1102    @SuppressWarnings("unchecked")
1103    private static Type asType(final AST ast, final String parentName, final ElementType type) {
1104
1105        if (type instanceof BasicType) {
1106
1107            return fromBasicType(ast, type);
1108
1109        } else if (type instanceof Array) {
1110
1111            final Type baseType = asType(ast, parentName, ((Array) type).getType());
1112
1113            final ParameterizedType result = ast
1114                    .newParameterizedType(ast.newSimpleType(ast.newName("java.util.List")));
1115
1116            result.typeArguments().add(baseType);
1117
1118            return result;
1119
1120        } else if (type instanceof Optional) {
1121
1122            final Type baseType = asType(ast, parentName, ((Optional) type).getType());
1123            final ParameterizedType result = ast
1124                    .newParameterizedType(ast.newSimpleType(ast.newName("java.util.Optional")));
1125
1126            result.typeArguments().add(baseType);
1127
1128            return result;
1129
1130        } else if (type instanceof Dictionary) {
1131
1132            final Type baseType = asType(ast, parentName, ((Dictionary) type).getType());
1133            final ParameterizedType result = ast
1134                    .newParameterizedType(ast.newSimpleType(ast.newName("java.util.Map")));
1135
1136            result.typeArguments().add(ast.newSimpleType(ast.newName("java.lang.String")));
1137            result.typeArguments().add(baseType);
1138
1139            return result;
1140
1141        } else if (type instanceof de.dentrassi.varlink.idl.varlinkIdl.Enum || type instanceof Object) {
1142
1143            // anonymous enum or object
1144
1145            return ast.newSimpleType(
1146                    ast.newSimpleName(toUpperFirst(parentName)));
1147
1148        } else if (type instanceof TypeReference) {
1149
1150            final String name = ((TypeReference) type).getName().getName();
1151            return ast.newSimpleType(ast.newSimpleName(toUpperFirst(name)));
1152
1153        }
1154        throw new IllegalArgumentException("Unsupported type: " + type.eClass().getName());
1155    }
1156
1157    private static Type fromBasicType(final AST ast, final ElementType type) {
1158        switch (((BasicType) type).getType().toLowerCase()) {
1159        case "float":
1160            return ast.newSimpleType(ast.newName("java.lang.Double"));
1161        case "int":
1162            return ast.newSimpleType(ast.newName("java.lang.Long"));
1163        case "bool":
1164            return ast.newSimpleType(ast.newName("java.lang.Boolean"));
1165        case "string":
1166            return ast.newSimpleType(ast.newName("java.lang.String"));
1167        default:
1168            throw new IllegalArgumentException("Unknown basic type: " + ((BasicType) type).getType().toLowerCase());
1169        }
1170    }
1171
1172    @SuppressWarnings("unchecked")
1173    private void makeAsync(final MethodDeclaration md) {
1174
1175        final AST ast = md.getAST();
1176
1177        final Type ret = md.getReturnType2();
1178
1179        final ParameterizedType future = ast
1180                .newParameterizedType(ast.newSimpleType(ast.newName("java.util.concurrent.CompletableFuture")));
1181
1182        if (ret instanceof PrimitiveType) {
1183            if (((PrimitiveType) ret).getPrimitiveTypeCode() == PrimitiveType.VOID) {
1184                future.typeArguments().add(ast.newSimpleType(ast.newName("java.lang.Void")));
1185            }
1186        } else {
1187            md.setReturnType2(null);
1188            future.typeArguments().add(ret);
1189        }
1190
1191        md.setReturnType2(future);
1192    }
1193
1194}