001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019package lucee.transformer.bytecode.statement;
020
021import java.util.ArrayList;
022import java.util.Iterator;
023import java.util.List;
024
025import lucee.transformer.bytecode.Body;
026import lucee.transformer.bytecode.BytecodeContext;
027import lucee.transformer.bytecode.BytecodeException;
028import lucee.transformer.bytecode.Position;
029import lucee.transformer.bytecode.expression.Expression;
030import lucee.transformer.bytecode.util.Types;
031
032import org.objectweb.asm.Label;
033import org.objectweb.asm.Type;
034import org.objectweb.asm.commons.GeneratorAdapter;
035import org.objectweb.asm.commons.Method;
036
037public final class Switch extends StatementBaseNoFinal implements FlowControlBreak,HasBodies {
038
039        // Object append(Object o)
040        private static final Method APPEND = new Method(
041                                                                                        "append",
042                                                                                        Types.OBJECT,
043                                                                                        new Type[]{Types.OBJECT}
044        );
045
046        public static final Method INIT = new Method(
047                        "<init>",
048                        Types.VOID,
049                        new Type[]{}
050                );
051
052        // int find(Array array, Object object)
053        private static final Method FIND = new Method(
054                        "find",
055                        Types.INT_VALUE,
056                        new Type[]{Types.ARRAY,Types.OBJECT}
057                );      
058        
059        private List<Case> cases=new ArrayList<Case>();
060    private Body defaultCase;
061        private Expression expr;
062
063        private NativeSwitch ns;
064
065
066    public Switch(Expression expr,Position start, Position end) {
067                super(start, end);
068                this.expr=expr;
069        }
070    
071    public void addCase(Expression expr, Body body) {
072        addCase(expr, body, null, null);
073    }
074    public void addCase(Expression expr, Body body,Position start,Position end) {
075        //if(cases==null) cases=new ArrayList();
076        cases.add(new Case(expr,body,start,end));
077        body.setParent(this);
078    }
079    public void setDefaultCase(Body body) {
080        defaultCase=body;
081        body.setParent(this);
082    }
083
084    
085    public final class Case {
086        private Expression expression;
087        private Body body;
088                private Position startPos;
089                private Position endPos;
090
091        public Case(Expression expression, Body body,Position start,Position end) {
092            this.expression=expression;
093            this.body=body;
094            this.startPos=start;
095            this.endPos=end;
096        }
097    }
098
099        /**
100         * @see lucee.transformer.bytecode.statement.StatementBase#_writeOut(org.objectweb.asm.commons.GeneratorAdapter)
101         */
102        public void _writeOut(BytecodeContext bc) throws BytecodeException {
103                GeneratorAdapter adapter = bc.getAdapter();
104                
105        // Array cases=new ArrayImpl();
106                int array=adapter.newLocal(Types.ARRAY);
107                adapter.newInstance(Types.ARRAY_IMPL);
108                adapter.dup();
109                adapter.invokeConstructor(Types.ARRAY_IMPL, INIT);
110                
111                adapter.storeLocal(array);
112                
113        // cases.append(case.value);
114                Iterator<Case> it = cases.iterator();
115                Case c;
116                while(it.hasNext()) {
117                        c=it.next();
118                        
119
120                        adapter.loadLocal(array);
121                        c.expression.writeOut(bc, Expression.MODE_REF);
122                        adapter.invokeVirtual(Types.ARRAY_IMPL, APPEND);
123                        adapter.pop();
124                }
125                
126                // int result=ArrayUtil.find(array,expression);
127                int result=adapter.newLocal(Types.INT_VALUE);
128                adapter.loadLocal(array);
129                expr.writeOut(bc, Expression.MODE_REF);
130                adapter.invokeStatic(Types.ARRAY_UTIL, FIND);
131                adapter.storeLocal(result);
132                
133                // switch(result)
134                ns=new NativeSwitch(result,NativeSwitch.LOCAL_REF,getStart(),getEnd());
135                it = cases.iterator();
136                int count=1;
137                while(it.hasNext()) {
138                        c=it.next();
139                        ns.addCase(count++, c.body,c.startPos,c.endPos,false);
140                }
141                if(defaultCase!=null)ns.addDefaultCase(defaultCase);
142                
143                ns.writeOut(bc);
144
145        }
146
147        /**
148         *
149         * @see lucee.transformer.bytecode.statement.FlowControl#getBreakLabel()
150         */
151        public Label getBreakLabel() {
152                return ns.getBreakLabel();
153        }
154
155        /**
156         *
157         * @see lucee.transformer.bytecode.statement.FlowControl#getContinueLabel()
158         */
159        public Label getContinueLabel() {
160                return ns.getContinueLabel();
161        }
162
163
164        /**
165         * @see lucee.transformer.bytecode.statement.HasBodies#getBodies()
166         */
167        public Body[] getBodies() {
168                if(cases==null) {
169                        if(defaultCase!=null) return new Body[]{defaultCase};
170                        return new Body[]{};
171                }
172                
173                int len=cases.size(),count=0;
174                if(defaultCase!=null)len++;
175                Body[] bodies=new Body[len];
176                Case c;
177                Iterator<Case> it = cases.iterator();
178                while(it.hasNext()) {
179                        c=it.next();
180                        bodies[count++]=c.body;
181                }
182                if(defaultCase!=null)bodies[count++]=defaultCase;
183                
184                return bodies;
185        }
186
187        @Override
188        public String getLabel() {
189                return null;
190        }
191
192}