1 /*
   2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 package sampleapi.generator;
  25 
  26 import java.io.File;
  27 import java.io.FileWriter;
  28 import java.io.InputStream;
  29 import java.io.FileInputStream;
  30 import java.io.IOException;
  31 import java.util.ArrayList;
  32 import java.util.Hashtable;
  33 import javax.xml.parsers.DocumentBuilderFactory;
  34 import javax.xml.parsers.DocumentBuilder;
  35 import javax.xml.parsers.ParserConfigurationException;
  36 import org.xml.sax.SAXException;
  37 import org.w3c.dom.Document;
  38 import org.w3c.dom.Element;
  39 import org.w3c.dom.Text;
  40 import org.w3c.dom.Node;
  41 import org.w3c.dom.NodeList;
  42 
  43 import com.sun.source.util.JavacTask;
  44 import com.sun.tools.javac.api.JavacTool;
  45 import com.sun.tools.javac.api.JavacTaskImpl;
  46 import com.sun.tools.javac.util.Context;
  47 import com.sun.tools.javac.util.Name;
  48 import com.sun.tools.javac.util.Names;
  49 import com.sun.tools.javac.util.List;
  50 import com.sun.tools.javac.util.ListBuffer;
  51 import com.sun.tools.javac.tree.TreeMaker;
  52 import com.sun.tools.javac.tree.JCTree;
  53 import com.sun.tools.javac.tree.JCTree.*;
  54 import com.sun.tools.javac.code.Flags;
  55 import com.sun.tools.javac.code.Type;
  56 import com.sun.tools.javac.code.TypeTag;
  57 import com.sun.tools.javac.code.TypeMetadata;
  58 import com.sun.tools.javac.code.Symbol;
  59 import com.sun.tools.javac.code.Symtab;
  60 
  61 import sampleapi.util.*;
  62 import sampleapi.SampleApi.Fault;
  63 
  64 public class PackageGenerator {
  65 
  66     String packageName;
  67     String packageDirName;
  68 
  69     ArrayList<JCCompilationUnit> topLevels;
  70     Hashtable<String, Integer> nameIndex;
  71     Hashtable<String, JCClassDecl> idBases;
  72     Hashtable<String, JCAnnotation> idAnnos;
  73 
  74     TreeMaker make;
  75     Names names;
  76     Symtab syms;
  77     DocumentBuilderFactory factory;
  78     Documentifier documentifier;
  79     boolean fx = false;
  80 
  81     public PackageGenerator() {
  82         JavacTool jt = JavacTool.create();
  83         JavacTask task = jt.getTask(null, null, null, null, null, null);
  84         Context ctx = ((JavacTaskImpl)task).getContext();
  85 
  86         make = TreeMaker.instance(ctx);
  87         names = Names.instance(ctx);
  88         syms = Symtab.instance(ctx);
  89         factory = DocumentBuilderFactory.newInstance();
  90 
  91         documentifier = Documentifier.instance(ctx);
  92     }
  93 
  94     String dataSetName;
  95 
  96     public void processDataSet(String dsName) throws Fault {
  97         dataSetName = dsName;
  98         topLevels = new ArrayList<>();
  99         nameIndex = new Hashtable<>();
 100         idBases =  new Hashtable<>();
 101         idAnnos =  new Hashtable<>();
 102 
 103         String dsPath = "res/xml/" + dsName + ".xml";
 104 
 105         try {
 106             InputStream is = getClass().getResourceAsStream("/" + dsPath);
 107             if (is == null)
 108                 is = new FileInputStream(dsPath);
 109 
 110             DocumentBuilder builder = factory.newDocumentBuilder();
 111             Document document = builder.parse(is);
 112 
 113             Element rootElement = document.getDocumentElement();
 114             packageName = rootElement.getAttribute("package");
 115             fx = "fx".equals(rootElement.getAttribute("style"));
 116             packageDirName = packageName.replace('.', '/');
 117 
 118             // process nodes (toplevels)
 119             NodeList nodeList = rootElement.getChildNodes();
 120             for (int i = 0; i < nodeList.getLength(); i++) {
 121                 Node node = nodeList.item(i);
 122 
 123                 if (!(node instanceof Element))
 124                     continue;
 125                 processTopLevel((Element)node);
 126             }
 127         } catch (ParserConfigurationException | SAXException | IOException e) {
 128             throw new Fault("Error parsing dataset " + dsName);
 129         }
 130 
 131         fx = false;
 132     }
 133 
 134     public void generate(File outDir) throws Fault {
 135         if (dataSetName == null)
 136             throw new Fault("No Data Set processed");
 137 
 138         try {
 139             File pkgDir = new File(outDir, packageDirName);
 140             pkgDir.mkdirs();
 141 
 142             for (JCCompilationUnit decl : topLevels) {
 143                 JCClassDecl classDecl = (JCClassDecl)decl.getTypeDecls().get(0);
 144                 File outFile = new File(pkgDir,
 145                                         classDecl.getSimpleName().toString() + ".java");
 146                 FileWriter writer = new FileWriter(outFile);
 147                 writer.write(decl.toString());
 148                 writer.flush();
 149                 writer.close();
 150             }
 151 
 152             // package-info
 153             File outFile = new File(pkgDir, "package-info.java");
 154             FileWriter writer = new FileWriter(outFile);
 155             writer.write("/**\n");
 156             writer.write(documentifier.getDocGenerator().getPackageComment());
 157             writer.write("*/\n");
 158             writer.write("package " + packageName + ";");
 159             writer.flush();
 160             writer.close();
 161         } catch (IOException e) {
 162             throw new Fault("Error writing output");
 163         }
 164     }
 165 
 166     // levels' processing methods
 167 
 168     void processTopLevel(Element tlTag) {
 169         String kind = tlTag.getTagName();
 170 
 171         if (kind.equals("annodecl")) {
 172             // decls stored separately, does not affect bases
 173             String declId = tlTag.getAttribute("id");
 174             if (!declId.startsWith("@"))
 175                 declId = "@" + declId;
 176             idAnnos.put(declId, processAnnoDecl(tlTag));
 177             return;
 178         }
 179 
 180         ListBuffer<JCTree>[] bases = processBases(tlTag, null);
 181 
 182         for (JCTree base : bases[0]) { // [0] - bases namely
 183             JCPackageDecl pkg = make.PackageDecl(
 184                                     List.<JCAnnotation>nil(),
 185                                     make.QualIdent(
 186                                         new Symbol.PackageSymbol(
 187                                             names.fromString(packageName),
 188                                             null)));
 189             ListBuffer<JCTree> topLevelParts = new ListBuffer<>();
 190             topLevelParts.append(pkg);
 191             topLevelParts.appendList(bases[1]); // [1] imports
 192             topLevelParts.append(base);
 193 
 194             JCCompilationUnit topLevel = make.TopLevel(topLevelParts.toList());
 195             documentifier.documentify(topLevel, fx);
 196             topLevels.add(topLevel);
 197         }
 198     }
 199 
 200     ListBuffer<JCTree>[] processBases(Element baseTag, Hashtable<String, Integer> scope) {
 201         String kind = baseTag.getTagName();
 202         String baseName = baseTag.getAttribute("basename");
 203         String typeParam = baseTag.getAttribute("tparam");
 204         String baseId = baseTag.getAttribute("id");
 205 
 206         long kindFlag = 0;
 207         switch (kind) {
 208             case "class":
 209                 // no flags for class
 210                 break;
 211             case "interface":
 212                 kindFlag |= Flags.INTERFACE;
 213                 break;
 214             case "enum":
 215                 kindFlag |= Flags.ENUM;
 216                 break;
 217             case "annotation":
 218                 kindFlag |= Flags.ANNOTATION | Flags.INTERFACE;
 219                 break;
 220         }
 221 
 222         // go through other nodes; add modifiers to multiplier
 223         NodeList nodes = baseTag.getChildNodes();
 224         ListBuffer<JCTree> bases = new ListBuffer<>();
 225         ListBuffer<JCTree> members = new ListBuffer<>();
 226         ListBuffer<JCTree> imports = new ListBuffer<>();
 227         JCExpression extType = null;
 228         ListBuffer<JCExpression> implTypes = new ListBuffer<>();
 229         SimpleMultiplier multiply = new SimpleMultiplier();
 230         for (int i = 0; i < nodes.getLength(); i++) {
 231             Node node = nodes.item(i);
 232 
 233             if (!(node instanceof Element))
 234                 continue;
 235 
 236             switch (((Element)node).getTagName()) {
 237                 case "modifier":
 238                     multiply.addAxis(((Element)node).getTextContent());
 239                     break;
 240                 case "anno":
 241                     multiply.addAxis(((Element)node).getTextContent());
 242                     break;
 243                 case "member":
 244                     // process members here
 245                     members.appendList(processMembers((Element)node, baseName, kind));
 246                     break;
 247                 case "extend":
 248                     String classId = ((Element)node).getAttribute("id");   // this pkg
 249                     String classRef = ((Element)node).getAttribute("ref"); // external
 250                     if (classId.length() !=0 &&
 251                         idBases.containsKey(classId)) {
 252                         // if have base, take methods from base members
 253                         JCClassDecl baseDecl = idBases.get(classId);
 254                         extType = make.Type(
 255                                       getTypeByName(
 256                                           baseDecl.getSimpleName().toString()));
 257                         members.appendList(processMethods(baseDecl.getMembers(), false));
 258                     } else if (classRef.length() !=0) {
 259                         extType = make.Type(getTypeByName(classRef));
 260                     }
 261                     break;
 262                 case "implement":
 263                     String interfaceId = ((Element)node).getAttribute("id");
 264                     String interfaceRef = ((Element)node).getAttribute("ref");
 265                     if (interfaceId.length() != 0 &&
 266                         idBases.containsKey(interfaceId)) {
 267                         JCClassDecl baseDecl = idBases.get(interfaceId);
 268                         implTypes.add(
 269                             make.Type(
 270                                 getTypeByName(
 271                                     baseDecl.getSimpleName().toString())));
 272                         members.appendList(processMethods(baseDecl.getMembers(), true));
 273                     } else if (interfaceRef.length() != 0) {
 274                         implTypes.add(make.Type(getTypeByName(interfaceRef)));
 275                     }
 276                     break;
 277                 case "import":
 278                     imports.append(
 279                         make.Import(
 280                             make.Ident(names.fromString(((Element)node).getTextContent())),
 281                             false));
 282             }
 283         }
 284 
 285         // process modifiers through multiplier
 286         multiply.initIterator();
 287         while (multiply.hasNext()) {
 288             ArrayList<String> tuple = multiply.getNext();
 289 
 290             long declFlags = kindFlag;
 291             ListBuffer<JCAnnotation> annos = new ListBuffer<>();
 292             for (String modifier : tuple) {
 293                 if (modifier.startsWith("@") && idAnnos.containsKey(modifier))
 294                     annos.add(idAnnos.get(modifier)); // it's anno
 295                 else
 296                     declFlags |= getFlagByName(modifier); // it's modifier
 297             }
 298 
 299             String declName = (scope == null)
 300                                   ? getUniqName(baseName)
 301                                   : baseName + getUniqIndex(scope, baseName);
 302             JCClassDecl baseDecl = make.ClassDef(
 303                                        make.Modifiers(declFlags, annos.toList()),
 304                                        names.fromString(declName),
 305                                        processTypeParams(typeParam), // type params
 306                                        extType,                      // ext
 307                                        implTypes.toList(),           // impl
 308                                        members.toList());            // members
 309 
 310             // fix constructors names
 311             fixConstructorNames(baseDecl);
 312 
 313             bases.append(baseDecl);
 314 
 315             // for non-empty ids store first base occurence from multiplied sequence
 316             if (baseId.length() != 0) {
 317                 idBases.put(baseId, baseDecl);
 318                 baseId = "";
 319             }
 320         }
 321 
 322         return new ListBuffer[] { bases, imports };
 323     }
 324 
 325     List<JCTypeParameter> processTypeParams(String typeParams) {
 326 
 327         if (typeParams == null || typeParams.length() == 0)
 328             return List.<JCTypeParameter>nil(); // empty
 329 
 330         String[] typeVarsArr = typeParams.split(",");
 331         ListBuffer<JCTypeParameter> typeParamsDecls = new ListBuffer<>();
 332 
 333         for (String typeVar : typeVarsArr) {
 334             typeParamsDecls.add(
 335                 make.TypeParameter(names.fromString(typeVar),
 336                                     List.<JCExpression>nil()));
 337         }
 338 
 339         return typeParamsDecls.toList();
 340     }
 341 
 342     ListBuffer<JCTree> processMembers(Element memberTag, String name, String kind) {
 343         ListBuffer<JCTree> members = new ListBuffer<>();
 344         NodeList nodes = memberTag.getChildNodes();
 345         Hashtable<String, Integer> scope = new Hashtable<>();
 346         for (int i = 0; i < nodes.getLength(); i++) {
 347             Node node = nodes.item(i);
 348 
 349             if (!(node instanceof Element))
 350                 continue;
 351 
 352             switch (((Element)node).getTagName()) {
 353                 case "field":
 354                     members.appendList(processFields((Element)node, scope));
 355                     break;
 356                 case "serialfield":
 357                     members.append(processSerialFields((Element)node));
 358                     break;
 359                 case "constant":
 360                     members.appendList(processConstants((Element)node, scope));
 361                     break;
 362                 case "constructor":
 363                     members.appendList(processMethods((Element)node, scope, true, true));
 364                     break;
 365                 case "method":
 366                     boolean needBody = kind.equals("class") || kind.equals("enum");
 367                     members.appendList(processMethods((Element)node, scope, needBody, false));
 368                     break;
 369                 case "class":
 370                 case "interface":
 371                 case "enum":
 372                 case "annotation":
 373                     members.appendList(processBases((Element)node, scope)[0]);
 374                     break;
 375             }
 376         }
 377 
 378         return members;
 379     }
 380 
 381     ListBuffer<JCTree> processFields(Element fieldsNode, Hashtable<String, Integer> scope) {
 382         String kind = fieldsNode.getTagName();
 383         String baseName = fieldsNode.getAttribute("basename");
 384 
 385         ListBuffer<JCTree> fields = new ListBuffer<>();
 386         NodeList nodes = fieldsNode.getChildNodes();
 387         SimpleMultiplier multiply = new SimpleMultiplier(); // for modifiers
 388         String[] types = new String[] {};
 389         for (int i = 0; i < nodes.getLength(); i++) {
 390             Node node = nodes.item(i);
 391 
 392             if (!(node instanceof Element))
 393                 continue;
 394 
 395             // parse type and modifiers
 396             switch (((Element)node).getTagName()) {
 397                 case "modifier":
 398                     multiply.addAxis(((Element)node).getTextContent());
 399                     break;
 400                 case "anno":
 401                     multiply.addAxis(((Element)node).getTextContent());
 402                 case "type":
 403                     types = ((Element)node).getTextContent().split("\\|");
 404                     break;
 405             }
 406         }
 407 
 408         // process through modifiers and types
 409         multiply.initIterator();
 410         while (multiply.hasNext()) {
 411             ArrayList<String> tuple = multiply.getNext();
 412 
 413             long declFlags = 0;
 414             ListBuffer<JCAnnotation> annos = new ListBuffer<>();
 415             for (String modifier : tuple) {
 416                 if (modifier.startsWith("@") && idAnnos.containsKey(modifier))
 417                     annos.add(idAnnos.get(modifier)); // it's anno
 418                 else
 419                     declFlags |= getFlagByName(modifier); // it's modifier
 420             }
 421 
 422 
 423             for (String type : types) {
 424                 String declName = baseName + getUniqIndex(scope, baseName);
 425 
 426                 Type initType = getTypeByName(type);
 427                 JCExpression initExpr = null;
 428                 if ((declFlags & Flags.STATIC) != 0) // static to be initialized
 429                     initExpr = make.Literal(initType.isPrimitive() ?
 430                                              initType.getTag() :
 431                                              TypeTag.BOT,
 432                                              "String".equals(type)
 433                                                  ? new String("blah-blah-blah")
 434                                                  : new Integer(0));
 435 
 436                 JCVariableDecl fieldDecl = make.VarDef(
 437                                                make.Modifiers(declFlags, annos.toList()),
 438                                                names.fromString(declName),
 439                                                make.Type(getTypeByName(type)),
 440                                                initExpr);
 441 
 442                 fields.append(fieldDecl);
 443             }
 444         }
 445 
 446         return fields;
 447     }
 448 
 449     JCTree processSerialFields(Element sfNode) {
 450         String baseName = sfNode.getAttribute("basename");
 451         String[] fieldTypes = sfNode.getTextContent().split(",");
 452 
 453         ListBuffer<JCExpression> serialFields = new ListBuffer<>();
 454         Hashtable<String, Integer> scope = new Hashtable<>();
 455 
 456         for (String fType : fieldTypes) {
 457             String fieldName = baseName + getUniqIndex(scope, baseName);
 458             serialFields.add(
 459                 make.NewClass(
 460                     null,
 461                     null,
 462                     make.Type(getTypeByName("ObjectStreamField")),
 463                     List.from(
 464                         new JCTree.JCExpression[] {
 465                             make.Literal(fieldName),
 466                             make.Ident(names.fromString(fType + ".class"))
 467                         }),
 468                     null));
 469         }
 470 
 471         JCTree sfDecl = make.VarDef(
 472                             make.Modifiers(
 473                                 Flags.PRIVATE | Flags.STATIC | Flags.FINAL),
 474                             names.fromString("serialPersistentFields"),
 475                             make.TypeArray(
 476                                 make.Type(getTypeByName("ObjectStreamField"))),
 477                             make.NewArray(
 478                                 null,
 479                                 List.<JCExpression>nil(),
 480                                 serialFields.toList()));
 481 
 482         return sfDecl;
 483     }
 484 
 485     ListBuffer<JCTree> processConstants(Element constNode, Hashtable<String, Integer> scope) {
 486         String baseName = constNode.getAttribute("basename");
 487         int count = 1;
 488         try {
 489             count = Integer.parseInt(constNode.getAttribute("count"));
 490         } catch (Exception e) {} // nothing to do, will use count = 1
 491 
 492         long declFlags = Flags.PUBLIC | Flags.STATIC | Flags.FINAL | Flags.ENUM;
 493         ListBuffer<JCTree> fields = new ListBuffer<>();
 494 
 495         for (int i = 0; i < count; i++) {
 496             String declName = baseName +
 497                               ((count == 1) ? "" : getUniqIndex(scope, baseName));
 498 
 499             JCVariableDecl constDecl = make.VarDef(
 500                                            make.Modifiers(declFlags),
 501                                            names.fromString(declName),
 502                                            null,  // no need for type in enum decl
 503                                            null); // no init
 504 
 505             fields.append(constDecl);
 506         }
 507         return fields;
 508     }
 509 
 510     ListBuffer<JCTree> processMethods(Element methodsNode, Hashtable<String, Integer> scope, boolean needBody, boolean isConstructor) {
 511         String kind = methodsNode.getTagName();
 512         String baseName = methodsNode.getAttribute("basename");
 513         String name = methodsNode.getAttribute("name");
 514         String methodTypeParam = methodsNode.getAttribute("tparam");
 515 
 516         ListBuffer<JCTree> methods = new ListBuffer<>();
 517         NodeList nodes = methodsNode.getChildNodes();
 518         SimpleMultiplier multiply = new SimpleMultiplier(); // for modifiers
 519         String[] types = new String[0];
 520         String[] params = new String[] { "none" }; // default - no params
 521         ListBuffer<Type> throwTypes = new ListBuffer<>();
 522         for (int i = 0; i < nodes.getLength(); i++) {
 523             Node node = nodes.item(i);
 524 
 525             if (!(node instanceof Element))
 526                 continue;
 527 
 528             // parse type and modifiers
 529             switch (((Element)node).getTagName()) {
 530                 case "modifier":
 531                     multiply.addAxis(((Element)node).getTextContent());
 532                     break;
 533                 case "anno":
 534                     multiply.addAxis(((Element)node).getTextContent());
 535                     break;
 536                 case "type":
 537                     types = ((Element)node).getTextContent().split("\\|");
 538                     break;
 539                 case "param":
 540                     params = ((Element)node).getTextContent().split("\\|");
 541                     break;
 542                 case "throw":
 543                     throwTypes.add(
 544                         getTypeByName(((Element)node).getTextContent()));
 545                     break;
 546 
 547             }
 548         }
 549 
 550         // constructor?
 551         if (isConstructor) {
 552             baseName = "constructor";
 553             types = new String[] { "" };
 554         }
 555 
 556         // direct name not indexed
 557         boolean isDirectName = false;
 558         if (name.length() > 0) {
 559             baseName = name;
 560             isDirectName = true;
 561         }
 562 
 563         // process through modifiers and types
 564         multiply.initIterator();
 565         while (multiply.hasNext()) {
 566             ArrayList<String> tuple = multiply.getNext();
 567 
 568             long declFlags = 0;
 569             ListBuffer<JCAnnotation> annos = new ListBuffer<>();
 570             for (String modifier : tuple) {
 571                 if (modifier.startsWith("@") && idAnnos.containsKey(modifier))
 572                     annos.add(idAnnos.get(modifier)); // it's anno
 573                 else
 574                     declFlags |= getFlagByName(modifier); // it's modifier
 575             }
 576 
 577             for (String type : types) {
 578                 String declName = baseName
 579                                   + ((isConstructor || isDirectName)
 580                                      ? "" : getUniqIndex(scope, baseName));
 581 
 582                 JCBlock body = null;
 583                 if (needBody && (declFlags & Flags.ABSTRACT) == 0) { // create body
 584                     List<JCStatement> bodyStatements = List.<JCStatement>nil();
 585                     if (!type.equals("") && !type.equals("void")) { // create return statement
 586                         Type retType = getTypeByName(type);
 587                         bodyStatements = List.<JCStatement>of(
 588                                              make.Return(
 589                                                  make.Literal(
 590                                                      retType.isPrimitive() ?
 591                                                          retType.getTag() :
 592                                                          TypeTag.BOT,
 593                                                      new Integer(0))));
 594                     }
 595                     body = make.Block(0, bodyStatements);
 596                 }
 597 
 598                 // same method by different params (if they exist)
 599                 for (String param : params) {
 600 
 601                     JCMethodDecl methodDecl =
 602                         make.MethodDef(
 603                             make.Modifiers(declFlags, annos.toList()),
 604                             names.fromString(declName),
 605                             isConstructor ? null : make.Type(getTypeByName(type)),
 606                             processTypeParams(methodTypeParam), // type params
 607                             null,                               // no receiver
 608                             processParams(param),               // formal params
 609                             make.Types(throwTypes.toList()),   // throws
 610                             body,
 611                             null);                              // no default value YET
 612 
 613                     methods.append(methodDecl);
 614                 }
 615             }
 616         }
 617 
 618         return methods;
 619     }
 620 
 621     JCAnnotation processAnnoDecl(Element annoDeclNode) {
 622         String annoId = annoDeclNode.getAttribute("id");
 623 
 624         ListBuffer<JCExpression> args = new ListBuffer<>();
 625         String className = "";
 626 
 627         NodeList nodes = annoDeclNode.getChildNodes();
 628         for (int i = 0; i < nodes.getLength(); i++) {
 629             Node node = nodes.item(i);
 630 
 631             if (!(node instanceof Element))
 632                 continue;
 633 
 634             switch (((Element)node).getTagName()) {
 635                 case "class":
 636                     className = ((Element)node).getTextContent();
 637                     break;
 638                 case "arg":
 639                     String argName = ((Element)node).getAttribute("name");
 640                     String argValue = ((Element)node).getAttribute("value");
 641 
 642                     JCExpression arg;
 643                     if (argName.length() == 0)
 644                         arg = make.Ident(names.fromString(argValue));
 645                     else
 646                         arg = make.Assign(
 647                                   make.Ident(names.fromString(argName)),
 648                                   make.Ident(names.fromString(argValue)));
 649 
 650                     args.add(arg);
 651                     break;
 652             }
 653         }
 654 
 655         return make.Annotation(
 656                    make.Ident(names.fromString(className)),
 657                    args.toList());
 658     }
 659 
 660     ListBuffer<JCTree> processMethods(List<JCTree> tree, boolean needBody) {
 661         // for "extends" clause; returns methods only
 662         ListBuffer<JCTree> methods = new ListBuffer<>();
 663         for (JCTree memberDecl : tree) {
 664             if (memberDecl instanceof JCMethodDecl) {
 665                 JCMethodDecl methodDecl = (JCMethodDecl)memberDecl;
 666                 JCTree retTypeTree = methodDecl.getReturnType();
 667 
 668                 // skip constructors
 669                 if (retTypeTree == null)
 670                     continue;
 671 
 672                 if (needBody) {
 673                     // here we need to 'implement' interface declared methods
 674                     Type retType = retTypeTree.type;
 675 
 676                     List<JCStatement> bodyStatements = List.<JCStatement>nil();
 677                     if (retType.getTag() != TypeTag.VOID)
 678                         bodyStatements = List.<JCStatement>of(
 679                                              make.Return(
 680                                                  make.Literal(
 681                                                      retType.isPrimitive() ?
 682                                                          retType.getTag() :
 683                                                          TypeTag.BOT,
 684                                                      new Integer(0))));
 685 
 686                     JCBlock body = make.Block(0, bodyStatements);
 687 
 688                     methodDecl = make.MethodDef(
 689                                      methodDecl.getModifiers(),
 690                                      methodDecl.getName(),
 691                                      (JCExpression)methodDecl.getReturnType(),
 692                                      methodDecl.getTypeParameters(),
 693                                      methodDecl.getReceiverParameter(),
 694                                      methodDecl.getParameters(),
 695                                      methodDecl.getThrows(),
 696                                      body,
 697                                      (JCExpression)methodDecl.getDefaultValue());
 698                 }
 699 
 700                 methods.add(methodDecl);
 701             }
 702         }
 703         return methods;
 704     }
 705 
 706     void fixConstructorNames(JCClassDecl baseDecl) {
 707         ListBuffer<JCTree> newMembers = new ListBuffer<>();
 708         List<JCTree> members = baseDecl.getMembers();
 709         Name name = baseDecl.getSimpleName();
 710 
 711         for (JCTree memberDecl : members) {
 712             JCTree newDecl = memberDecl;
 713 
 714             if (memberDecl instanceof JCMethodDecl) {
 715                 JCMethodDecl methodDecl = (JCMethodDecl)memberDecl;
 716                 JCTree retTypeTree = methodDecl.getReturnType();
 717 
 718                 if (retTypeTree == null)
 719                     newDecl = make.MethodDef(
 720                                   methodDecl.getModifiers(),
 721                                   name,
 722                                   (JCExpression)methodDecl.getReturnType(),
 723                                   methodDecl.getTypeParameters(),
 724                                   methodDecl.getReceiverParameter(),
 725                                   methodDecl.getParameters(),
 726                                   methodDecl.getThrows(),
 727                                   methodDecl.getBody(),
 728                                   (JCExpression)methodDecl.getDefaultValue());
 729             }
 730 
 731             newMembers.add(newDecl);
 732         }
 733 
 734         baseDecl.defs = newMembers.toList();
 735     }
 736 
 737     List<JCVariableDecl> processParams(String paramTypes) {
 738 
 739         if ("none".equals(paramTypes))
 740             return List.<JCVariableDecl>nil(); // empty
 741 
 742         String[] typesArr = paramTypes.split(",(?!(\\w+,)*\\w+>)");
 743         ListBuffer<JCVariableDecl> paramsDecls = new ListBuffer<>();
 744 
 745         int i = 0;
 746         for (String typeName : typesArr) {
 747             String paramName = "param"
 748                                + (typesArr.length == 1 ? "" : String.valueOf(i));
 749             paramsDecls.add(
 750                 make.VarDef(make.Modifiers(0),
 751                              names.fromString(paramName),
 752                              make.Type(getTypeByName(typeName)),
 753                              null));
 754             i++;
 755         }
 756 
 757         return paramsDecls.toList();
 758     }
 759 
 760     //
 761     // util methods
 762     //
 763 
 764     String getUniqName(String name) {
 765         if (!nameIndex.containsKey(name))
 766             nameIndex.put(name, new Integer(0));
 767         Integer index = nameIndex.get(name);
 768         String uniqName = name + index;
 769         nameIndex.put(name, index + 1);
 770         return uniqName;
 771     }
 772 
 773     int getUniqIndex(Hashtable<String, Integer> scope, String name) {
 774         if (!scope.containsKey(name))
 775             scope.put(name, new Integer(0));
 776         Integer index = scope.get(name);
 777         scope.put(name, index + 1);
 778         return index;
 779     }
 780 
 781     long getFlagByName(String modifierName) {
 782         switch (modifierName) {
 783             case "public":
 784                 return Flags.PUBLIC;
 785             case "private":
 786                 return Flags.PRIVATE;
 787             case "protected":
 788                 return Flags.PROTECTED;
 789             case "static":
 790                 return Flags.STATIC;
 791             case "final":
 792                 return Flags.FINAL;
 793             case "abstract":
 794                 return Flags.ABSTRACT;
 795             case "strictfp":
 796                 return Flags.STRICTFP;
 797             default:
 798                 return 0;
 799         }
 800     }
 801 
 802     Type getTypeByName(String typeName) {
 803         //check for primitive types
 804         switch (typeName) {
 805             case "void":
 806                 return syms.voidType;
 807             case "boolean":
 808                 return syms.booleanType;
 809             case "byte":
 810                 return syms.byteType;
 811             case "char":
 812                 return syms.charType;
 813             case "double":
 814                 return syms.doubleType;
 815             case "float":
 816                 return syms.floatType;
 817             case "int":
 818                 return syms.intType;
 819             case "long":
 820                 return syms.longType;
 821             default:
 822                 return getTypeByName(typeName, List.<Type>nil());
 823         }
 824     }
 825 
 826     Type getTypeByName(String typeName, List<Type> tparams) {
 827         return new Type.ClassType(
 828                    Type.noType,
 829                    tparams,
 830                    new Symbol.ClassSymbol(0, names.fromString(typeName), null));
 831     }
 832 }