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 Enums {
032    
033      /*
034         1) It is a compile-time error to attempt to explicitly instantiate an enum type
035         (�15.9.1).
036      */
037      syn boolean TypeDecl.isEnumDecl() = false;
038      eq EnumDecl.isEnumDecl() = true;
039    
040      refine NameCheck public void ClassInstanceExpr.nameCheck() {
041        if (getAccess().type().isEnumDecl() && !enclosingBodyDecl().isEnumConstant()) {
042          error("enum types may not be instantiated explicitly");
043        } else {
044          refined();
045        }
046      }
047    
048      syn boolean BodyDecl.isEnumConstant() = false;
049      eq EnumConstant.isEnumConstant() = true;
050    
051      /*
052        5) Enum types (�8.9) must not be declared abstract; doing so will result in a
053        compile-time error.
054      */
055      eq EnumDecl.getModifiers().mayBeAbstract() = false;
056    
057      /*
058        9) Nested enum types are implicitly static. It is permissable to explicitly
059        declare a nested enum type to be static.
060      */
061      eq EnumDecl.isStatic() = isNestedType();
062      eq EnumDecl.getModifiers().mayBeStatic() = isNestedType();
063    
064      /*
065        12) It is a compile-time error for an enum to declare a finalizer. An instance of
066        an enum may never be finalized.
067      */
068      public void EnumDecl.typeCheck() {
069        super.typeCheck();
070        for (Iterator iter = memberMethods("finalize").iterator(); iter.hasNext(); ) {
071          MethodDecl m = (MethodDecl) iter.next();
072          if (m.getNumParameter() == 0 && m.hostType() == this) {
073            error("an enum may not declare a finalizer");
074          }
075        }
076        checkEnum(this);
077      }
078    
079      /*
080        10) The direct superclass of an enum type named E is Enum<E>.
081      */
082    
083      syn lazy Opt EnumDecl.getSuperClassOpt() {
084        return new Opt(
085          new ParTypeAccess(
086            new TypeAccess(
087              "java.lang",
088              "Enum"
089            ),
090            new List().add(createQualifiedAccess())
091          )
092        );
093      }
094    
095      /*
096        3b) If the enum type has no constructor declarations, a parameterless default
097        constructor is provided (which matches the implicit empty argument list).
098        This default constructor is private.
099      */
100    
101    
102      eq ParameterDeclaration.getTypeAccess().nameType() = NameType.TYPE_NAME;
103    
104      // TODO Remove
105      private boolean EnumDecl.done = false;
106      private boolean EnumDecl.done() {
107        if (done) {
108          return true;
109        }
110        done = true;
111        return false;
112      }
113    
114      // TODO Remove
115      rewrite EnumDecl {
116        when (!done())
117        to EnumDecl {
118          transformEnumConstructors();
119          addValues(); // Add the values() and getValue(String s) methods
120          return this;
121        }
122      }
123    
124      syn lazy Opt<ConstructorDecl> EnumDecl.getImplicitConstructorOpt() {
125        if (needsImplicitConstructor()) {
126          List parameterList = new List();
127          parameterList.add(
128              new ParameterDeclaration(new TypeAccess("java.lang", "String"), "p0")
129          );
130          parameterList.add(
131              new ParameterDeclaration(new TypeAccess("int"), "p1")
132          );
133          ConstructorDecl constructor = new ConstructorDecl(
134              new Modifiers(new List()
135                .add(new Modifier("private"))
136                .add(new Modifier("synthetic"))
137                ),
138              name(),
139              parameterList,
140              new List(),
141              new Opt(
142                new ExprStmt(
143                  new SuperConstructorAccess(
144                    "super",
145                    new List()
146                    .add(new VarAccess("p0"))
147                    .add(new VarAccess("p1"))
148                    )
149                  )
150                ),
151              new Block(new List())
152              );
153          return new Opt<ConstructorDecl>(constructor);
154        } else {
155          return new Opt<ConstructorDecl>();
156        }
157      }
158    
159      protected void ASTNode.transformEnumConstructors() {
160        for (int i = 0; i < getNumChildNoTransform(); i++) {
161          ASTNode child = getChildNoTransform(i);
162          if (child != null) {
163            child.transformEnumConstructors();
164          }
165        }
166      }
167    
168      refine LookupConstructor
169      eq ConstructorDecl.getConstructorInvocation() {
170        if (!isSynthetic() && hostType().isEnumDecl()) {
171          return getImplicitConstructorInvocation();
172        }
173        return refined();
174      }
175    
176      refine LookupConstructor
177      eq ConstructorDecl.getImplicitConstructorInvocation() {
178        if (hostType().isEnumDecl()) {
179          ConstructorAccess newAccess;
180          if (hasParsedConstructorInvocation()) {
181            ExprStmt stmt = (ExprStmt) getParsedConstructorInvocation();
182            ConstructorAccess access = (ConstructorAccess) stmt.getExpr();
183            newAccess = (ConstructorAccess) access.treeCopyNoTransform();
184          } else {
185            newAccess = new SuperConstructorAccess("super", new List());
186          }
187          if (!hostType().original().typeName().equals("java.lang.Enum")) {
188            // java.lang.Enum calls the java.lang.Object constructor, with no extra params
189            newAccess.getArgList().insertChild(new VarAccess("@p0"),0);
190            newAccess.getArgList().insertChild(new VarAccess("@p1"),1);
191          }
192          return new ExprStmt(newAccess);
193        }
194        return refined();
195      }
196    
197      protected void ConstructorDecl.transformEnumConstructors() {
198        super.transformEnumConstructors();
199        getParameterList().insertChild(
200          new ParameterDeclaration(new TypeAccess("java.lang", "String"), "@p0"),
201          0
202        );
203        getParameterList().insertChild(
204          new ParameterDeclaration(new TypeAccess("int"), "@p1"),
205          1
206        );
207      }
208    
209      /*
210        11) In addition to the members it inherits from Enum<E>, for each declared
211        enum constant with the name n the enum type has an implicitly declared
212        public static final field named n of type E. These fields are considered to
213        be declared in the same order as the corresponding enum constants, before
214        any static fields explicitly declared in the enum type. Each such field is
215        initialized to the enum constant that corresponds to it. Each such field is
216        also considered to be annotated by the same annotations as the
217        corresponding enum constant. The enum constant is said to be created when
218        the corresponding field is initialized.
219      */
220    
221      eq EnumConstant.isPublic() = true;
222      eq EnumConstant.isStatic() = true;
223      eq EnumConstant.isFinal() = true;
224    
225      syn lazy Access EnumConstant.getTypeAccess() {
226        return hostType().createQualifiedAccess();
227      }
228    
229      public EnumConstant.EnumConstant(Modifiers mods, String name, List<Expr> args, List<BodyDecl> bds) {
230        this(mods, name, args, new Opt<Expr>(new EnumInstanceExpr(createOptAnonymousDecl(bds))));
231      }
232    
233      /*
234        3) An enum constant may be followed by arguments, which are passed to the
235        constructor of the enum type when the constant is created during class
236        initialization as described later in this section. The constructor to be
237        invoked is chosen using the normal overloading rules (�15.12.2). If the
238        arguments are omitted, an empty argument list is assumed.
239      */
240    
241      syn lazy Access EnumInstanceExpr.getAccess() {
242        return hostType().createQualifiedAccess();
243      }
244    
245      syn lazy List<Expr> EnumInstanceExpr.getArgList() {
246        EnumConstant ec = (EnumConstant) getParent().getParent();
247        List<EnumConstant> ecs = (List<EnumConstant>)ec.getParent();
248        int idx = ecs.getIndexOfChild(ec);
249        if (idx == -1) {
250          throw new Error("internal: cannot determine numeric value of enum constant");
251        }
252        List<Expr> argList = new List<Expr>();
253        argList.add(Literal.buildStringLiteral(ec.name()));
254        argList.add(Literal.buildIntegerLiteral(idx));
255        for (Expr arg : ec.getArgs()) {
256          argList.add((Expr) arg.treeCopyNoTransform());
257        }
258        return argList;
259      }
260    
261      /*
262        4) The optional class body of an enum constant implicitly defines an anonymous
263        class declaration (�15.9.5) that extends the immediately enclosing enum type.
264        The class body is governed by the usual rules of anonymous classes; in
265        particular it cannot contain any constructors.
266    
267        TODO: work on error messages
268      */
269    
270      private static Opt<TypeDecl> EnumConstant.createOptAnonymousDecl(List<BodyDecl> bds) {
271        if (bds.getNumChildNoTransform() == 0) {
272          return new Opt<TypeDecl>();
273        }
274        return new Opt<TypeDecl>(
275          new AnonymousDecl(
276            new Modifiers(),
277            "Anonymous",
278            bds
279          )
280        );
281      }
282    
283      // simulate list of body declarations
284      public int EnumConstant.getNumBodyDecl() {
285        int cnt = 0;
286        ClassInstanceExpr init = (ClassInstanceExpr) getInit();
287        if (!init.hasTypeDecl()) {
288          return 0;
289        }
290        for (BodyDecl bd : init.getTypeDecl().getBodyDecls()) {
291          if (!(bd instanceof ConstructorDecl)) {
292            ++cnt;
293          }
294        }
295        return cnt;
296      }
297    
298      public BodyDecl EnumConstant.getBodyDecl(int i) {
299        ClassInstanceExpr init = (ClassInstanceExpr) getInit();
300        if (init.hasTypeDecl()) {
301          for (BodyDecl bd : init.getTypeDecl().getBodyDecls()) {
302            if (!(bd instanceof ConstructorDecl)) {
303              if (i-- == 0) {
304                return bd;
305              }
306            }
307          }
308        }
309        throw new ArrayIndexOutOfBoundsException(i);
310      }
311    
312      /*
313        7) It is a compile-time error for the class body of an enum constant to declare
314        an abstract method.
315    
316        TODO: work on error messages
317      */
318    
319      /*
320        8) An enum type is implicitly final unless it contains at least one enum
321        constant that has a class body. In any case, it is a compile-time error to
322        explicitly declare an enum type to be final.
323      */
324    
325      eq EnumDecl.isFinal() {
326        for (Iterator iter = enumConstants().iterator(); iter.hasNext(); ) {
327          EnumConstant c = (EnumConstant) iter.next();
328          ClassInstanceExpr e = (ClassInstanceExpr) c.getInit();
329          if (e.hasTypeDecl()) {
330            return false;
331          }
332        }
333        return true;
334      }
335      eq EnumDecl.getModifiers().mayBeFinal() = false;
336    
337      syn lazy ArrayList EnumDecl.enumConstants() {
338        ArrayList list = new ArrayList();
339        for (int i = 0; i < getNumBodyDecl(); i++) {
340          if (getBodyDecl(i).isEnumConstant()) {
341            list.add(getBodyDecl(i));
342          }
343        }
344        return list;
345      }
346    
347      /*
348        13) In addition, if E is the name of an enum type, then that type has the
349        following implicitly declared static methods:
350          public static E[] values();
351          public static E valueOf(String name);
352      */
353    
354      private void EnumDecl.addValues() {
355        int numConstants = enumConstants().size();
356        List initValues = new List();
357        for (Iterator iter = enumConstants().iterator(); iter.hasNext(); ) {
358          EnumConstant c = (EnumConstant) iter.next();
359          initValues.add(c.createBoundFieldAccess());
360        }
361        FieldDeclaration values = new FieldDeclaration(
362          new Modifiers(new List().add(
363            new Modifier("private")).add(
364            new Modifier("static")).add(
365            new Modifier("final")).add(
366            new Modifier("synthetic"))
367          ),
368          arrayType().createQualifiedAccess(),
369          "$VALUES",
370          new Opt(
371              new ArrayCreationExpr(
372                new ArrayTypeWithSizeAccess(
373                  createQualifiedAccess(),
374                  Literal.buildIntegerLiteral(enumConstants().size())
375                ),
376                new Opt(
377                  new ArrayInit(
378                    initValues
379                  )
380                )
381              )
382          )
383        );
384        addBodyDecl(values);
385        // public static final Test[] values() { return (Test[])$VALUES.clone(); }
386        addBodyDecl(
387          new MethodDecl(
388            new Modifiers(new List().add(
389              new Modifier("public")).add(
390              new Modifier("static")).add(
391              new Modifier("final")).add(
392              new Modifier("synthetic"))
393            ),
394            arrayType().createQualifiedAccess(),
395            "values",
396            new List(),
397            new List(),
398            new Opt(
399              new Block(
400                new List().add(
401                  new ReturnStmt(
402                    new Opt(
403                      new CastExpr(
404                        arrayType().createQualifiedAccess(),
405                        values.createBoundFieldAccess().qualifiesAccess(
406                          new MethodAccess(
407                            "clone",
408                            new List()
409                          )
410                        )
411                      )
412                    )
413                  )
414                )
415              )
416            )
417          )
418        );
419        // public static Test valueOf(String s) { return (Test) java.lang.Enum.valueOf(Test.class, s); }
420        addBodyDecl(
421          new MethodDecl(
422            new Modifiers(new List().add(
423              new Modifier("public")).add(
424              new Modifier("static")).add(
425              new Modifier("synthetic"))
426            ),
427            createQualifiedAccess(),
428            "valueOf",
429            new List().add(
430              new ParameterDeclaration(
431                new Modifiers(new List()),
432                typeString().createQualifiedAccess(),
433                "s"
434              )
435            ),
436            new List(),
437            new Opt(
438              new Block(
439                new List().add(
440                  new ReturnStmt(
441                    new Opt(
442                      new CastExpr(
443                        createQualifiedAccess(),
444                        lookupType("java.lang", "Enum").createQualifiedAccess().qualifiesAccess(
445                          new MethodAccess(
446                            "valueOf",
447                            new List().add(
448                              createQualifiedAccess().qualifiesAccess(new ClassAccess())
449                            ).add(
450                              new VarAccess(
451                                "s"
452                              )
453                            )
454                          )
455                        )
456                      )
457                    )
458                  )
459                )
460              )
461            )
462          )
463        );
464      }
465    
466      inh TypeDecl EnumDecl.typeString();
467    
468      /*
469        14) It is a compile-time error to reference a static field of an enum type that
470        is not a compile-time constant (�15.28) from constructors, instance
471        initializer blocks, or instance variable initializer expressions of that
472        type.
473      */
474    
475      /**
476       * @return true if the enum decl contains an abstract method declaration
477       */
478      eq EnumDecl.isAbstract() {
479        for (int i = 0; i < getNumBodyDecl(); i++) {
480          if (getBodyDecl(i) instanceof MethodDecl) {
481            MethodDecl m = (MethodDecl) getBodyDecl(i);
482            if (m.isAbstract()) {
483              return true;
484            }
485          }
486        }
487        return false;
488      }
489    
490      protected void ASTNode.checkEnum(EnumDecl enumDecl) {
491        for (int i = 0; i < getNumChild(); i++) {
492          getChild(i).checkEnum(enumDecl);
493        }
494      }
495    
496      protected void EnumDecl.checkEnum(EnumDecl enumDecl) {
497        for (int i = 0; i < getNumBodyDecl(); i++) {
498          if (getBodyDecl(i) instanceof ConstructorDecl) {
499            getBodyDecl(i).checkEnum(enumDecl);
500          } else if (getBodyDecl(i) instanceof InstanceInitializer) {
501            getBodyDecl(i).checkEnum(enumDecl);
502          } else if (getBodyDecl(i) instanceof FieldDeclaration) {
503            FieldDeclaration f = (FieldDeclaration) getBodyDecl(i);
504            if (!f.isStatic() && f.hasInit()) {
505              f.checkEnum(enumDecl);
506            }
507          }
508        }
509      }
510    
511      protected void TypeDecl.checkEnum(EnumDecl enumDecl) {
512        // non-EnumDecl TypeDecls should not check enum stuff
513      }
514    
515      protected void VarAccess.checkEnum(EnumDecl enumDecl) {
516        super.checkEnum(enumDecl);
517        if (decl().isStatic() && decl().hostType() == enumDecl && !isConstant()) {
518          error("may not reference a static field of an enum type from here");
519        }
520      }
521    
522      /**
523       * Check if the enum constructor has an incorrect access modifier
524       */
525      protected void ConstructorDecl.checkEnum(EnumDecl enumDecl) {
526        super.checkEnum(enumDecl);
527    
528        if (isPublic()) {
529          error("enum constructors can not be declared public");
530        } else if (isProtected()) {
531          error("enum constructors can not be declared public");
532        }
533    
534        if (hasParsedConstructorInvocation()) {
535          ExprStmt invocation = (ExprStmt) getParsedConstructorInvocation();
536          if (invocation.getExpr() instanceof SuperConstructorAccess) {
537            error("can not call super() in enum constructor");
538          }
539        }
540      }
541    
542      /*
543        15) It is a compile-time error for the constructors, instance initializer blocks,
544        or instance variable initializer expressions of an enum constant e to refer
545        to itself or to an enum constant of the same type that is declared to the
546        right of e.
547    
548      traversal that checks for errors
549      */
550    
551    
552    
553      // 8.9
554    
555      /* 2) An enum constant may be preceded by annotation (�9.7) modifiers. If an
556      annotation a on an enum constant corresponds to an annotation type T, and T
557      has a (meta-)annotation m that corresponds to annotation.Target, then m must
558      have an element whose value is annotation.ElementType.FIELD, or a
559      compile-time error occurs.
560      Comment: This is done in Annotations.jrag
561      */
562    
563    
564      eq EnumConstant.getTypeAccess().nameType() = NameType.TYPE_NAME;
565    
566      refine TypeCheck public void SwitchStmt.typeCheck() {
567         TypeDecl type = getExpr().type();
568        if ((!type.isIntegralType() || type.isLong()) && !type.isEnumDecl()) {
569          error("Switch expression must be of char, byte, short, int, or enum type");
570        }
571      }
572    
573      eq ConstCase.getValue().lookupVariable(String name)
574        = switchType().isEnumDecl() ? switchType().memberFields(name) : lookupVariable(name);
575    
576      syn boolean Expr.isEnumConstant() = false;
577      eq VarAccess.isEnumConstant() = varDecl() instanceof EnumConstant;
578    
579      refine TypeCheck public void ConstCase.typeCheck() {
580        boolean isEnumConstant = getValue().isEnumConstant();
581        if (switchType().isEnumDecl() && !isEnumConstant) {
582          error("Unqualified enumeration constant required");
583        } else {
584          TypeDecl switchType = switchType();
585          TypeDecl type = getValue().type();
586          if (!type.assignConversionTo(switchType, getValue())) {
587            error("Constant expression must be assignable to Expression");
588          }
589          if (!getValue().isConstant() && !getValue().type().isUnknown() &&
590              !isEnumConstant)
591            error("Switch expression must be constant");
592        }
593      }
594      refine NameCheck eq ConstCase.constValue(Case c) {
595        if (switchType().isEnumDecl()) {
596          if (!(c instanceof ConstCase) || !getValue().isConstant()) {
597            return false;
598          }
599          return getValue().varDecl() == ((ConstCase) c).getValue().varDecl();
600        } else {
601          return refined(c);
602        }
603      }
604    
605      public void EnumDecl.prettyPrint(PrettyPrinter out) {
606        if (!docComment.isEmpty()) {
607          out.print(docComment);
608        }
609        out.print(getModifiers());
610        out.print("enum " + name());
611        if (getNumImplements() > 0) {
612          out.print(" implements ");
613          out.join(getImplementsList(), new PrettyPrinter.Joiner() {
614            @Override
615            public void printSeparator(PrettyPrinter out) {
616              out.print(", ");
617            }
618          });
619        }
620        out.print(" {");
621        for (int i = 0; i < getNumBodyDecl(); i++) {
622          BodyDecl d = getBodyDecl(i);
623          out.print(d);
624          if (d instanceof EnumConstant) {
625            if (i + 1 < getNumBodyDecl() && !(getBodyDecl(i + 1) instanceof EnumConstant)) {
626              out.println();
627              out.print(";");
628            }
629          }
630        }
631        out.println();
632        out.print("}");
633      }
634    
635      public void EnumConstant.prettyPrint(PrettyPrinter out) {
636        out.println();
637        out.print(getModifiers());
638        out.print(getID());
639        out.print("(");
640        if (getNumArg() > 0) {
641          out.join(getArgList(), new PrettyPrinter.Joiner() {
642            @Override
643            public void printSeparator(PrettyPrinter out) {
644              out.print(", ");
645            }
646          });
647        }
648        out.print(")");
649        if (getNumBodyDecl() > 0) {
650          out.print(" {");
651          for (int i=0; i < getNumBodyDecl(); i++) {
652            BodyDecl d = getBodyDecl(i);
653            out.print(d);
654          }
655          out.print("}");
656        }
657        out.print(",");
658      }
659    
660      /**
661       * From the Java Language Specification, third edition, section 8.9 Enums:
662       *
663       * It is a compile-time error for an enum type E to have an abstract method
664       * m as a member unless E has one or more enum constants, and all of E's enum
665       * constants have class bodies that provide concrete implementations of m.
666       */
667      eq EnumDecl.unimplementedMethods() {
668        Collection<MethodDecl> methods = new LinkedList<MethodDecl>();
669        for (Iterator iter = interfacesMethodsIterator(); iter.hasNext(); ) {
670          MethodDecl method = (MethodDecl) iter.next();
671          SimpleSet set = (SimpleSet) localMethodsSignature(method.signature());
672          if (set.size() == 1) {
673            MethodDecl n = (MethodDecl) set.iterator().next();
674            if (!n.isAbstract()) {
675              continue;
676            }
677          }
678          boolean implemented = false;
679          set = (SimpleSet) ancestorMethods(method.signature());
680          for (Iterator i2 = set.iterator(); i2.hasNext(); ) {
681            MethodDecl n = (MethodDecl) i2.next();
682            if (!n.isAbstract()) {
683              implemented = true;
684              break;
685            }
686          }
687          if (!implemented) {
688            methods.add(method);
689          }
690        }
691    
692        for (Iterator iter = localMethodsIterator(); iter.hasNext(); ) {
693          MethodDecl method = (MethodDecl) iter.next();
694          if (method.isAbstract()) {
695            methods.add(method);
696          }
697        }
698    
699        Collection unimplemented = new ArrayList();
700        for (MethodDecl method : methods) {
701          if (enumConstants().isEmpty()) {
702            unimplemented.add(method);
703            continue;
704          }
705          boolean missing = false;
706          for (Iterator iter = enumConstants().iterator(); iter.hasNext(); ) {
707            if (!((EnumConstant) iter.next()).implementsMethod(method)) {
708              missing = true;
709              break;
710            }
711          }
712          if (missing) {
713            unimplemented.add(method);
714          }
715        }
716    
717        return unimplemented;
718      }
719    
720      /**
721       * Check that the enum does not contain unimplemented abstract methods.
722       */
723      public void EnumDecl.checkModifiers() {
724        super.checkModifiers();
725        if (!unimplementedMethods().isEmpty()) {
726          StringBuilder sb = new StringBuilder();
727          sb.append("" + name() + " lacks implementations in one or more "
728              + "enum constants for the following methods:\n");
729          for (Iterator iter = unimplementedMethods().iterator(); iter.hasNext(); ) {
730            MethodDecl m = (MethodDecl) iter.next();
731            sb.append("  " + m.signature() + " in " + m.hostType().typeName() + "\n");
732          }
733          error(sb.toString());
734        }
735      }
736    
737      syn SimpleSet EnumConstant.localMethodsSignature(String signature) {
738        SimpleSet set = localMethodsSignatureMap().get(signature);
739        if (set != null) {
740          return set;
741        }
742        return SimpleSet.emptySet;
743      }
744    
745      // signature -> method declaration
746      syn lazy Map<String,SimpleSet> EnumConstant.localMethodsSignatureMap() {
747        HashMap map = new HashMap(getNumBodyDecl());
748        for (int i = 0; i < getNumBodyDecl(); i++) {
749          if (getBodyDecl(i) instanceof MethodDecl) {
750            MethodDecl decl = (MethodDecl) getBodyDecl(i);
751            putSimpleSetElement(map, decl.signature(), decl);
752          }
753        }
754        return map;
755      }
756    
757      syn boolean EnumConstant.implementsMethod(MethodDecl method) {
758        SimpleSet set = (SimpleSet) localMethodsSignature(method.signature());
759        if (set.size() == 1) {
760          MethodDecl n = (MethodDecl) set.iterator().next();
761          if (!n.isAbstract()) {
762            return true;
763          }
764        }
765        return false;
766      }
767    
768      refine Modifiers
769      public void MethodDecl.checkModifiers() {
770        super.checkModifiers();
771        if (hostType().isClassDecl()) {
772          // 8.4.3.1
773          if (!hostType().isEnumDecl() && isAbstract() && !hostType().isAbstract()) {
774            error("class must be abstract to include abstract methods");
775          }
776          // 8.4.3.1
777          if (isAbstract() && isPrivate()) {
778            error("method may not be abstract and private");
779          }
780          // 8.4.3.1
781          // 8.4.3.2
782          if (isAbstract() && isStatic()) {
783            error("method may not be abstract and static");
784          }
785          if (isAbstract() && isSynchronized()) {
786            error("method may not be abstract and synchronized");
787          }
788          // 8.4.3.4
789          if (isAbstract() && isNative()) {
790            error("method may not be abstract and native");
791          }
792          if (isAbstract() && isStrictfp()) {
793            error("method may not be abstract and strictfp");
794          }
795          if (isNative() && isStrictfp()) {
796            error("method may not be native and strictfp");
797          }
798        }
799        if (hostType().isInterfaceDecl()) {
800          // 9.4
801          if (isStatic()) {
802            errorf("interface method %s in %s may not be static", signature(), hostType().typeName());
803          }
804          if (isStrictfp()) {
805            errorf("interface method %s in %s may not be strictfp", signature(), hostType().typeName());
806          }
807          if (isNative()) {
808            errorf("interface method %s in %s may not be native", signature(), hostType().typeName());
809          }
810          if (isSynchronized()) {
811            errorf("interface method %s in %s may not be synchronized", signature(),
812                hostType().typeName());
813          }
814          if (isProtected()) {
815            errorf("interface method %s in %s may not be protected", signature(),
816                hostType().typeName());
817          }
818          if (isPrivate()) {
819            errorf("interface method %s in %s may not be private", signature(), hostType().typeName());
820          } else if (isFinal()) {
821            errorf("interface method %s in %s may not be final", signature(), hostType().typeName());
822          }
823        }
824      }
825    }