001    /*
002     * JastAddJ is covered by the modified BSD License. You should have received a copy of the
003     * modified BSD license with this compiler.
004     * 
005     * Copyright (c) 2011, Jesper Öqvist <jesper.oqvist@cs.lth.se>
006     * All rights reserved.
007     */
008    
009    /**
010     * <p>This aspect adds the Project Coin/JSR 334 Strings in Switch language change
011     * to the JastAddJ frontend.
012     *
013     * <p>The following features were modified:
014     * <ul>
015     * <li>type checking of the switch expression</li>
016     * <li>duplicate checking of switch labels</li>
017     * </ul>
018     */
019    aspect StringsInSwitch {
020        /**
021         * <p>Overrides the type checking of the switch statement's expression.
022         *
023         * <p>In JSR 334 a switch statement may use an expression of type String.
024         */
025        refine Enums public void SwitchStmt.typeCheck() {
026                TypeDecl type = getExpr().type();
027                if ((!type.isIntegralType() || type.isLong()) && !type.isEnumDecl()
028                                && !type.isString())
029                        error("Switch expression must be of type " +
030                                        "char, byte, short, int, enum, or string");
031        }
032    
033        /* Type checking for case labels need not be changed as it
034         * already tests if the case labels have expressions which are
035         * assignable to the switch expression
036         */
037    
038        syn boolean Case.isDefaultCase() = false;
039        eq DefaultCase.isDefaultCase() = true;
040    
041        /**
042         * <p>This attribute overrides the default duplicate checking for
043         * switch case labels.
044         */
045        refine Enums eq ConstCase.constValue(Case c) {
046                if (isDefaultCase() || c.isDefaultCase())
047                        return isDefaultCase() && c.isDefaultCase();
048    
049                Expr myValue = getValue();
050                Expr otherValue = ((ConstCase) c).getValue();
051                TypeDecl myType = myValue.type();
052                TypeDecl otherType = otherValue.type();
053                if (myType.isString() || otherType.isString()) {
054                        if (!myType.isString() || !otherType.isString())
055                                return false;
056                        if (!myValue.isConstant() || !otherValue.isConstant())
057                                return false;
058                        return myValue.constant().stringValue().equals(
059                                        otherValue.constant().stringValue());
060                }
061    
062                return refined(c);
063        }
064    
065        /**
066         * <p>Improve the type checking error messages given for case labels.
067         */
068        refine Enums public void ConstCase.typeCheck() {
069                boolean isEnumConstant = getValue().isEnumConstant();
070                TypeDecl switchType = switchType();
071                TypeDecl type = getValue().type();
072                if (switchType.isEnumDecl() && !isEnumConstant)
073                        error("Unqualified enumeration constant required");
074                if (!type.assignConversionTo(switchType, getValue()))
075                        error("Case label has incompatible type "+switchType.name()+
076                                        ", expected type compatible with "+type.name());
077                if (!getValue().isConstant() && !getValue().type().isUnknown() &&
078                                !isEnumConstant) 
079                        error("Case label must have constant expression");
080        }
081    
082    }