001    /* Copyright (c) 2005-2008, Torbjorn Ekman
002     * All rights reserved.
003     *
004     * Redistribution and use in source and binary forms, with or without
005     * modification, are permitted provided that the following conditions are met:
006     *
007     * 1. Redistributions of source code must retain the above copyright notice,
008     * this list of conditions and the following disclaimer.
009     *
010     * 2. Redistributions in binary form must reproduce the above copyright notice,
011     * this list of conditions and the following disclaimer in the documentation
012     * and/or other materials provided with the distribution.
013     *
014     * 3. Neither the name of the copyright holder nor the names of its
015     * contributors may be used to endorse or promote products derived from this
016     * software without specific prior written permission.
017     *
018     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021     * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
022     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
023     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
024     * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
025     * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
026     * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
027     * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
028     * POSSIBILITY OF SUCH DAMAGE.
029     */
030    
031    aspect EnumsCodegen {
032        // add flags to enums
033      public static final int Modifiers.ACC_ENUM = 0x4000;
034      eq EnumDecl.flags() = super.flags() | Modifiers.ACC_ENUM;
035      eq EnumConstant.flags() = super.flags() | Modifiers.ACC_ENUM;
036    
037      // transform enum switch statements into integer indexed switch statements
038      public void SwitchStmt.transformation() {
039        if (getExpr().type().isEnumDecl()) {
040          TypeDecl type = getExpr().type();
041          hostType().createEnumArray(type);
042          hostType().createEnumMethod(type);
043          setExpr(
044            hostType().createEnumMethod(type).createBoundAccess(new List()).qualifiesAccess(
045            new ArrayAccess(
046              ((Expr) getExpr().treeCopyNoTransform()).qualifiesAccess(new MethodAccess("ordinal", new List()))
047            ))
048          );
049        }
050        super.transformation();
051      }
052    
053      public void ConstCase.transformation() {
054        if (getValue() instanceof VarAccess && getValue().varDecl() instanceof EnumConstant) {
055          int i = hostType().createEnumIndex((EnumConstant) getValue().varDecl());
056          setValue(Literal.buildIntegerLiteral(i));
057        }
058        super.transformation();
059      }
060    
061    
062      // static method with lazy initalization
063      syn lazy MethodDecl TypeDecl.createEnumMethod(TypeDecl enumDecl) {
064        MethodDecl m = new MethodDecl(
065          new Modifiers(new List().add(new Modifier("static")).add(new Modifier("final")).add(new Modifier("private"))),
066          typeInt().arrayType().createQualifiedAccess(),
067          "$SwitchMap$" + enumDecl.fullName().replace('.', '$'),
068          new List(),
069          new List(),
070          new Opt(
071            new Block(
072              new List().add(
073                new IfStmt(
074                  new EQExpr(
075                    createEnumArray(enumDecl).createBoundFieldAccess(),
076                    new NullLiteral("null")
077                  ),
078                  AssignExpr.asStmt(
079                    createEnumArray(enumDecl).createBoundFieldAccess(),
080                    new ArrayCreationExpr(
081                      new ArrayTypeWithSizeAccess(
082                        typeInt().createQualifiedAccess(),
083                        enumDecl.createQualifiedAccess().qualifiesAccess(
084                            new MethodAccess("values", new List())).qualifiesAccess(
085                            new VarAccess("length"))
086                      ),
087                      new Opt()
088                    )
089                  ),
090                  new Opt()
091                )
092              ).add(
093                new ReturnStmt(
094                  createEnumArray(enumDecl).createBoundFieldAccess()
095                )
096              )
097            )
098          )
099        );
100        // add method declaration as a body declaration
101        getBodyDeclList().insertChild(m, 1);
102        // trigger possible rewrites
103        return (MethodDecl) getBodyDeclList().getChild(1);
104      }
105      // compute index of enum constants
106      private HashMap TypeDecl.createEnumIndexMap = null;
107      syn lazy int TypeDecl.createEnumIndex(EnumConstant e) {
108        if (createEnumIndexMap == null) {
109          createEnumIndexMap = new HashMap();
110        }
111        if (!createEnumIndexMap.containsKey(e.hostType())) {
112          createEnumIndexMap.put(e.hostType(), new Integer(0));
113        }
114        Integer i = (Integer) createEnumIndexMap.get(e.hostType());
115        i = new Integer(i.intValue() + 1);
116        createEnumIndexMap.put(e.hostType(), i);
117    
118        MethodDecl m = createEnumMethod(e.hostType());
119        List list = m.getBlock().getStmtList();
120        list.insertChild(
121          new TryStmt(
122            new Block(
123              new List().add(
124                AssignExpr.asStmt(
125                  createEnumArray(e.hostType()).createBoundFieldAccess().qualifiesAccess(
126                    new ArrayAccess(
127                      e.createBoundFieldAccess().qualifiesAccess(new MethodAccess("ordinal", new List()))
128                    )
129                  ),
130                  Literal.buildIntegerLiteral(i.intValue())
131                )
132              )
133            ),
134            new List().add(
135              new BasicCatch(
136                new ParameterDeclaration(
137                  lookupType("java.lang", "NoSuchFieldError").createQualifiedAccess(),
138                  "e"
139                ),
140                new Block(
141                  new List()
142                )
143              )
144            ),
145            new Opt()
146          ),
147          list.getNumChild()-1
148        );
149        return i.intValue();
150      }
151      // static field with array contents
152      syn lazy FieldDeclaration TypeDecl.createEnumArray(TypeDecl enumDecl) {
153        FieldDeclaration f = new FieldDeclaration(
154          new Modifiers(new List().add(new Modifier("static")).add(new Modifier("final")).add(new Modifier("private"))),
155          typeInt().arrayType().createQualifiedAccess(),
156          "$SwitchMap$" + enumDecl.fullName().replace('.', '$'),
157          new Opt()
158        );
159        // add field declaration as a body declaration
160        getBodyDeclList().insertChild(f, 0);
161        // trigger possible rewrites
162        return (FieldDeclaration) getBodyDeclList().getChild(0);
163      }
164    }