001    package railo.transformer.bytecode.statement;
002    
003    import java.util.ArrayList;
004    import java.util.Iterator;
005    import java.util.List;
006    
007    import org.objectweb.asm.Label;
008    import org.objectweb.asm.Type;
009    import org.objectweb.asm.commons.GeneratorAdapter;
010    import org.objectweb.asm.commons.Method;
011    
012    import railo.transformer.bytecode.Body;
013    import railo.transformer.bytecode.BytecodeContext;
014    import railo.transformer.bytecode.BytecodeException;
015    import railo.transformer.bytecode.Position;
016    import railo.transformer.bytecode.expression.Expression;
017    import railo.transformer.bytecode.util.Types;
018    
019    public final class Switch extends StatementBaseNoFinal implements FlowControlBreak,HasBodies {
020        
021            private static final Type ARRAY_IMPL=Type.getType(railo.runtime.type.ArrayImpl.class);
022    
023            // Object append(Object o)
024            private static final Method APPEND = new Method(
025                                                                                            "append",
026                                                                                            Types.OBJECT,
027                                                                                            new Type[]{Types.OBJECT}
028            );
029    
030            private static final Method INIT = new Method(
031                            "<init>",
032                            Types.VOID,
033                            new Type[]{}
034                    );
035    
036            // int find(Array array, Object object)
037            private static final Method FIND = new Method(
038                            "find",
039                            Types.INT_VALUE,
040                            new Type[]{Types.ARRAY,Types.OBJECT}
041                    );      
042            
043            private List<Case> cases=new ArrayList<Case>();
044        private Body defaultCase;
045            private Expression expr;
046    
047            private NativeSwitch ns;
048    
049    
050        public Switch(Expression expr,Position start, Position end) {
051                    super(start, end);
052                    this.expr=expr;
053            }
054        
055        public void addCase(Expression expr, Body body) {
056            addCase(expr, body, null, null);
057        }
058        public void addCase(Expression expr, Body body,Position start,Position end) {
059            //if(cases==null) cases=new ArrayList();
060            cases.add(new Case(expr,body,start,end));
061            body.setParent(this);
062        }
063        public void setDefaultCase(Body body) {
064            defaultCase=body;
065            body.setParent(this);
066        }
067    
068        
069        public final class Case {
070            private Expression expression;
071            private Body body;
072                    private Position startPos;
073                    private Position endPos;
074    
075            public Case(Expression expression, Body body,Position start,Position end) {
076                this.expression=expression;
077                this.body=body;
078                this.startPos=start;
079                this.endPos=end;
080            }
081        }
082    
083            /**
084             * @see railo.transformer.bytecode.statement.StatementBase#_writeOut(org.objectweb.asm.commons.GeneratorAdapter)
085             */
086            public void _writeOut(BytecodeContext bc) throws BytecodeException {
087                    GeneratorAdapter adapter = bc.getAdapter();
088                    
089            // Array cases=new ArrayImpl();
090                    int array=adapter.newLocal(Types.ARRAY);
091                    adapter.newInstance(ARRAY_IMPL);
092                    adapter.dup();
093                    adapter.invokeConstructor(ARRAY_IMPL, INIT);
094                    
095                    adapter.storeLocal(array);
096                    
097            // cases.append(case.value);
098                    Iterator<Case> it = cases.iterator();
099                    Case c;
100                    while(it.hasNext()) {
101                            c=it.next();
102                            
103    
104                            adapter.loadLocal(array);
105                            c.expression.writeOut(bc, Expression.MODE_REF);
106                            adapter.invokeVirtual(ARRAY_IMPL, APPEND);
107                            adapter.pop();
108                    }
109                    
110                    // int result=ArrayUtil.find(array,expression);
111                    int result=adapter.newLocal(Types.INT_VALUE);
112                    adapter.loadLocal(array);
113                    expr.writeOut(bc, Expression.MODE_REF);
114                    adapter.invokeStatic(Types.ARRAY_UTIL, FIND);
115                    adapter.storeLocal(result);
116                    
117                    // switch(result)
118                    ns=new NativeSwitch(result,NativeSwitch.LOCAL_REF,getStart(),getEnd());
119                    it = cases.iterator();
120                    int count=1;
121                    while(it.hasNext()) {
122                            c=it.next();
123                            ns.addCase(count++, c.body,c.startPos,c.endPos,false);
124                    }
125                    if(defaultCase!=null)ns.addDefaultCase(defaultCase);
126                    
127                    ns.writeOut(bc);
128    
129            }
130    
131            /**
132             *
133             * @see railo.transformer.bytecode.statement.FlowControl#getBreakLabel()
134             */
135            public Label getBreakLabel() {
136                    return ns.getBreakLabel();
137            }
138    
139            /**
140             *
141             * @see railo.transformer.bytecode.statement.FlowControl#getContinueLabel()
142             */
143            public Label getContinueLabel() {
144                    return ns.getContinueLabel();
145            }
146    
147    
148            /**
149             * @see railo.transformer.bytecode.statement.HasBodies#getBodies()
150             */
151            public Body[] getBodies() {
152                    if(cases==null) {
153                            if(defaultCase!=null) return new Body[]{defaultCase};
154                            return new Body[]{};
155                    }
156                    
157                    int len=cases.size(),count=0;
158                    if(defaultCase!=null)len++;
159                    Body[] bodies=new Body[len];
160                    Case c;
161                    Iterator<Case> it = cases.iterator();
162                    while(it.hasNext()) {
163                            c=it.next();
164                            bodies[count++]=c.body;
165                    }
166                    if(defaultCase!=null)bodies[count++]=defaultCase;
167                    
168                    return bodies;
169            }
170    
171            @Override
172            public String getLabel() {
173                    return null;
174            }
175    
176    }