public JCTree visitStringSwitch(JCSwitch tree) {
        List<JCCase> caseList = tree.getCases();
        int alternatives = caseList.size();

        if (alternatives == 0) { // Strange but legal possibility
            return make.at(tree.pos()).Exec(attr.makeNullCheck(tree.getExpression()));
        } else {
            /*
             * The general approach used is to translate a single
             * string switch statement into a series of two chained
             * switch statements: the first a synthesized statement
             * switching on the argument string's hash value and
             * computing a string's position in the list of original
             * case labels, if any, followed by a second switch on the
             * computed integer value.  The second switch has the same
             * code structure as the original string switch statement
             * except that the string case labels are replaced with
             * positional integer constants starting at 0.
             *
             * The first switch statement can be thought of as an
             * inlined map from strings to their position in the case
             * label list.  An alternate implementation would use an
             * actual Map for this purpose, as done for enum switches.
             *
             * With some additional effort, it would be possible to
             * use a single switch statement on the hash code of the
             * argument, but care would need to be taken to preserve
             * the proper control flow in the presence of hash
             * collisions and other complications, such as
             * fallthroughs.  Switch statements with one or two
             * alternatives could also be specially translated into
             * if-then statements to omit the computation of the hash
             * code.
             *
             * The generated code assumes that the hashing algorithm
             * of String is the same in the compilation environment as
             * in the environment the code will run in.  The string
             * hashing algorithm in the SE JDK has been unchanged
             * since at least JDK 1.2.  Since the algorithm has been
             * specified since that release as well, it is very
             * unlikely to be changed in the future.
             *
             * Different hashing algorithms, such as the length of the
             * strings or a perfect hashing algorithm over the
             * particular set of case labels, could potentially be
             * used instead of String.hashCode.
             */

            ListBuffer<JCStatement> stmtList = new ListBuffer<JCStatement>();

            // Map from String case labels to their original position in
            // the list of case labels.
            Map<String, Integer> caseLabelToPosition =
                new LinkedHashMap<String, Integer>(alternatives + 1, 1.0f);

            // Map of hash codes to the string case labels having that hashCode.
            Map<Integer, Set<String>> hashToString =
                new LinkedHashMap<Integer, Set<String>>(alternatives + 1, 1.0f);

            int casePosition = 0;
            for(JCCase oneCase : caseList) {
                JCExpression expression = oneCase.getExpression();

                if (expression != null) { // expression for a "default" case is null
                    expression = TreeInfo.skipParens(expression);
                    String labelExpr = (String) expression.type.constValue();
                    Integer mapping = caseLabelToPosition.put(labelExpr, casePosition);
                    Assert.checkNull(mapping);
                    int hashCode = labelExpr.hashCode();

                    Set<String> stringSet = hashToString.get(hashCode);
                    if (stringSet == null) {
                        stringSet = new LinkedHashSet<String>(1, 1.0f);
                        stringSet.add(labelExpr);
                        hashToString.put(hashCode, stringSet);
                    } else {
                        boolean added = stringSet.add(labelExpr);
                        Assert.check(added);
                    }
                }
                casePosition++;
            }

            // Synthesize a switch statement that has the effect of
            // mapping from a string to the integer position of that
            // string in the list of case labels.  This is done by
            // switching on the hashCode of the string followed by an
            // if-then-else chain comparing the input for equality
            // with all the case labels having that hash value.

            /*
             * s$ = top of stack;
             * tmp$ = -1;
             * switch($s.hashCode()) {
             *     case caseLabel.hashCode:
             *         if (s$.equals("caseLabel_1")
             *           tmp$ = caseLabelToPosition("caseLabel_1");
             *         else if (s$.equals("caseLabel_2"))
             *           tmp$ = caseLabelToPosition("caseLabel_2");
             *         ...
             *         break;
             * ...
             * }
             */

            VarSymbol dollar_s = new VarSymbol(FINAL|SYNTHETIC,
                                               names.fromString("s" + tree.pos + target.syntheticNameChar()),
                                               syms.stringType,
                                               currentMethodSym);
            stmtList.append(make.at(tree.pos()).VarDef(dollar_s, tree.getExpression()).setType(dollar_s.type));

            VarSymbol dollar_tmp = new VarSymbol(SYNTHETIC,
                                                 names.fromString("tmp" + tree.pos + target.syntheticNameChar()),
                                                 syms.intType,
                                                 currentMethodSym);
            JCVariableDecl dollar_tmp_def =
                (JCVariableDecl)make.VarDef(dollar_tmp, make.Literal(INT, -1)).setType(dollar_tmp.type);
            dollar_tmp_def.init.type = dollar_tmp.type = syms.intType;
            stmtList.append(dollar_tmp_def);
            ListBuffer<JCCase> caseBuffer = ListBuffer.lb();
            // hashCode will trigger nullcheck on original switch expression
            JCMethodInvocation hashCodeCall = makeCall(make.Ident(dollar_s),
                                                       names.hashCode,
                                                       List.<JCExpression>nil()).setType(syms.intType);
            JCSwitch switch1 = make.Switch(hashCodeCall,
                                        caseBuffer.toList());
            for(Map.Entry<Integer, Set<String>> entry : hashToString.entrySet()) {
                int hashCode = entry.getKey();
                Set<String> stringsWithHashCode = entry.getValue();
                Assert.check(stringsWithHashCode.size() >= 1);

                JCStatement elsepart = null;
                for(String caseLabel : stringsWithHashCode ) {
                    JCMethodInvocation stringEqualsCall = makeCall(make.Ident(dollar_s),
                                                                   names.equals,
                                                                   List.<JCExpression>of(make.Literal(caseLabel)));
                    elsepart = make.If(stringEqualsCall,
                                       make.Exec(make.Assign(make.Ident(dollar_tmp),
                                                             make.Literal(caseLabelToPosition.get(caseLabel))).
                                                 setType(dollar_tmp.type)),
                                       elsepart);
                }

                ListBuffer<JCStatement> lb = ListBuffer.lb();
                JCBreak breakStmt = make.Break(null);
                breakStmt.target = switch1;
                lb.append(elsepart).append(breakStmt);

                caseBuffer.append(make.Case(make.Literal(hashCode), lb.toList()));
            }

            switch1.cases = caseBuffer.toList();
            stmtList.append(switch1);

            // Make isomorphic switch tree replacing string labels
            // with corresponding integer ones from the label to
            // position map.

            ListBuffer<JCCase> lb = ListBuffer.lb();
            JCSwitch switch2 = make.Switch(make.Ident(dollar_tmp), lb.toList());
            for(JCCase oneCase : caseList ) {
                // Rewire up old unlabeled break statements to the
                // replacement switch being created.
                patchTargets(oneCase, tree, switch2);

                boolean isDefault = (oneCase.getExpression() == null);
                JCExpression caseExpr;
                if (isDefault)
                    caseExpr = null;
                else {
                    caseExpr = make.Literal(caseLabelToPosition.get((String)TreeInfo.skipParens(oneCase.
                                                                                                getExpression()).
                                                                    type.constValue()));
                }

                lb.append(make.Case(caseExpr,
                                    oneCase.getStatements()));
            }

            switch2.cases = lb.toList();
            stmtList.append(switch2);

            return make.Block(0L, stmtList.toList());
        }
    }