001    /*
002     * The JastAdd Extensible Java Compiler (http://jastadd.org) is covered
003     * by the modified BSD License. You should have received a copy of the
004     * modified BSD license with this compiler.
005     * 
006     * Copyright (c) 2005-2008, Torbjorn Ekman
007     * All rights reserved.
008     */
009    
010    aspect EnumsCodegen {
011        // add flags to enums
012      public static final int Modifiers.ACC_ENUM = 0x4000;
013      eq EnumDecl.flags() = super.flags() | Modifiers.ACC_ENUM;
014      eq EnumConstant.flags() = super.flags() | Modifiers.ACC_ENUM;
015    
016      // transform enum switch statements into integer indexed switch statements
017      public void SwitchStmt.transformation() {
018        if(getExpr().type().isEnumDecl()) {
019          TypeDecl type = getExpr().type();
020          hostType().createEnumArray(type);
021          hostType().createEnumMethod(type);
022          setExpr(
023            hostType().createEnumMethod(type).createBoundAccess(new List()).qualifiesAccess(
024            new ArrayAccess(
025              ((Expr)getExpr().fullCopy()).qualifiesAccess(new MethodAccess("ordinal", new List()))
026            ))
027          );
028        }
029        super.transformation();
030      }
031    
032      public void ConstCase.transformation() {
033        if(getValue() instanceof VarAccess && getValue().varDecl() instanceof EnumConstant) {
034          int i = hostType().createEnumIndex((EnumConstant)getValue().varDecl());
035          setValue(Literal.buildIntegerLiteral(i));
036        }
037        super.transformation();
038      }
039    
040    
041      // static method with lazy initalization
042      syn lazy MethodDecl TypeDecl.createEnumMethod(TypeDecl enumDecl) {
043        MethodDecl m = new MethodDecl(
044          new Modifiers(new List().add(new Modifier("static")).add(new Modifier("final")).add(new Modifier("private"))),
045          typeInt().arrayType().createQualifiedAccess(),
046          "$SwitchMap$" + enumDecl.fullName().replace('.', '$'),
047          new List(),
048          new List(),
049          new Opt(
050            new Block(
051              new List().add(
052                new IfStmt(
053                  new EQExpr(
054                    createEnumArray(enumDecl).createBoundFieldAccess(),
055                    new NullLiteral("null")
056                  ),
057                  AssignExpr.asStmt(
058                    createEnumArray(enumDecl).createBoundFieldAccess(),
059                    new ArrayCreationExpr(
060                      new ArrayTypeWithSizeAccess(
061                        typeInt().createQualifiedAccess(),
062                        enumDecl.createQualifiedAccess().qualifiesAccess(
063                            new MethodAccess("values", new List())).qualifiesAccess(
064                            new VarAccess("length"))
065                      ),
066                      new Opt()
067                    )
068                  ),
069                  new Opt()
070                )
071              ).add(
072                new ReturnStmt(
073                  createEnumArray(enumDecl).createBoundFieldAccess()
074                )
075              )
076            )
077          )
078        );
079        // add method declaration as a body declaration
080        getBodyDeclList().insertChild(m, 1);
081        // trigger possible rewrites
082        return (MethodDecl)getBodyDeclList().getChild(1);
083      }
084      // compute index of enum constants
085      private HashMap TypeDecl.createEnumIndexMap = null;
086      syn lazy int TypeDecl.createEnumIndex(EnumConstant e) {
087        if(createEnumIndexMap == null)
088          createEnumIndexMap = new HashMap();
089        if(!createEnumIndexMap.containsKey(e.hostType()))
090          createEnumIndexMap.put(e.hostType(), new Integer(0));
091        Integer i = (Integer)createEnumIndexMap.get(e.hostType());
092        i = new Integer(i.intValue() + 1);
093        createEnumIndexMap.put(e.hostType(), i);
094    
095        MethodDecl m = createEnumMethod(e.hostType());
096        List list = m.getBlock().getStmtList();
097        list.insertChild(
098          new TryStmt(
099            new Block(
100              new List().add(
101                AssignExpr.asStmt(
102                  createEnumArray(e.hostType()).createBoundFieldAccess().qualifiesAccess(
103                    new ArrayAccess(
104                      e.createBoundFieldAccess().qualifiesAccess(new MethodAccess("ordinal", new List()))
105                    )
106                  ),
107                  Literal.buildIntegerLiteral(i.intValue())
108                )
109              )
110            ),
111            new List().add(
112              new BasicCatch(
113                new ParameterDeclaration(
114                  lookupType("java.lang", "NoSuchFieldError").createQualifiedAccess(),
115                  "e"
116                ),
117                new Block(
118                  new List()
119                )
120              )
121            ),
122            new Opt()
123          ),
124          list.getNumChild()-1
125        );
126        return i.intValue();
127      }
128      // static field with array contents
129      syn lazy FieldDeclaration TypeDecl.createEnumArray(TypeDecl enumDecl) {
130        FieldDeclaration f = new FieldDeclaration(
131          new Modifiers(new List().add(new Modifier("static")).add(new Modifier("final")).add(new Modifier("private"))),
132          typeInt().arrayType().createQualifiedAccess(),
133          "$SwitchMap$" + enumDecl.fullName().replace('.', '$'),
134          new Opt()
135        );
136        // add field declaration as a body declaration
137        getBodyDeclList().insertChild(f, 0);
138        // trigger possible rewrites
139        return (FieldDeclaration)getBodyDeclList().getChild(0);
140      }
141    }