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}