001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements. See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership. The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the  "License");
007     * you may not use this file except in compliance with the License.
008     * You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    /*
019     * $Id: Choose.java 1225842 2011-12-30 15:14:35Z mrglavas $
020     */
021    
022    package org.apache.xalan.xsltc.compiler;
023    
024    import java.util.ArrayList;
025    import java.util.Enumeration;
026    import java.util.Iterator;
027    import java.util.List;
028    
029    import org.apache.bcel.generic.BranchHandle;
030    import org.apache.bcel.generic.GOTO;
031    import org.apache.bcel.generic.IFEQ;
032    import org.apache.bcel.generic.InstructionHandle;
033    import org.apache.bcel.generic.InstructionList;
034    import org.apache.xalan.xsltc.compiler.util.ClassGenerator;
035    import org.apache.xalan.xsltc.compiler.util.ErrorMsg;
036    import org.apache.xalan.xsltc.compiler.util.MethodGenerator;
037    import org.apache.xalan.xsltc.compiler.util.Type;
038    import org.apache.xalan.xsltc.compiler.util.TypeCheckError;
039    import org.apache.xalan.xsltc.compiler.util.Util;
040    
041    /**
042     * @author Jacek Ambroziak
043     * @author Santiago Pericas-Geertsen
044     * @author Morten Jorgensen
045     */
046    final class Choose extends Instruction {
047    
048        /**
049         * Display the element contents (a lot of when's and an otherwise)
050         */
051        public void display(int indent) {
052            indent(indent);
053            Util.println("Choose");
054            indent(indent + IndentIncrement);
055            displayContents(indent + IndentIncrement);
056        }
057            
058        /**
059         * Translate this Choose element. Generate a test-chain for the various
060         * <xsl:when> elements and default to the <xsl:otherwise> if present.
061         */
062        public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
063            final List whenElements = new ArrayList();
064            Otherwise otherwise = null;
065            Enumeration elements = elements();
066    
067            // These two are for reporting errors only
068            ErrorMsg error = null;
069            final int line = getLineNumber();
070    
071            // Traverse all child nodes - must be either When or Otherwise
072            while (elements.hasMoreElements()) {
073                Object element = elements.nextElement();
074                // Add a When child element
075                if (element instanceof When) {
076                    whenElements.add(element);
077                }
078                // Add an Otherwise child element
079                else if (element instanceof Otherwise) {
080                    if (otherwise == null) {
081                        otherwise = (Otherwise)element;
082                    }
083                    else {
084                        error = new ErrorMsg(ErrorMsg.MULTIPLE_OTHERWISE_ERR, this);
085                        getParser().reportError(Constants.ERROR, error);
086                    }
087                }
088                else if (element instanceof Text) {
089                    ((Text)element).ignore();
090                }
091                // It is an error if we find some other element here
092                else {
093                    error = new ErrorMsg(ErrorMsg.WHEN_ELEMENT_ERR, this);
094                    getParser().reportError(Constants.ERROR, error);
095                }
096            }
097    
098            // Make sure that there is at least one <xsl:when> element
099            if (whenElements.size() == 0) {
100                error = new ErrorMsg(ErrorMsg.MISSING_WHEN_ERR, this);
101                getParser().reportError(Constants.ERROR, error);
102                return;
103            }
104    
105            InstructionList il = methodGen.getInstructionList();
106    
107            // next element will hold a handle to the beginning of next
108            // When/Otherwise if test on current When fails
109            BranchHandle nextElement = null;
110            List exitHandles = new ArrayList();
111            InstructionHandle exit = null;
112    
113            Iterator whens = whenElements.iterator();
114            while (whens.hasNext()) {
115                final When when = (When)whens.next();
116                final Expression test = when.getTest();
117    
118                InstructionHandle truec = il.getEnd();
119    
120                if (nextElement != null) 
121                    nextElement.setTarget(il.append(NOP));
122                test.translateDesynthesized(classGen, methodGen);
123    
124                if (test instanceof FunctionCall) {
125                    FunctionCall call = (FunctionCall)test;
126                    try {
127                        Type type = call.typeCheck(getParser().getSymbolTable());
128                        if (type != Type.Boolean) {
129                            test._falseList.add(il.append(new IFEQ(null)));
130                        }
131                    }
132                    catch (TypeCheckError e) { 
133                        // handled later!
134                    }
135                }
136                // remember end of condition
137                truec = il.getEnd();
138    
139                // The When object should be ignored completely in case it tests
140                // for the support of a non-available element
141                if (!when.ignore()) when.translateContents(classGen, methodGen);
142    
143                // goto exit after executing the body of when
144                exitHandles.add(il.append(new GOTO(null)));
145                if (whens.hasNext() || otherwise != null) {
146                    nextElement = il.append(new GOTO(null));
147                    test.backPatchFalseList(nextElement);
148                }
149                else
150                    test.backPatchFalseList(exit = il.append(NOP));
151                test.backPatchTrueList(truec.getNext());
152            }
153            
154            // Translate any <xsl:otherwise> element
155            if (otherwise != null) {
156                nextElement.setTarget(il.append(NOP));
157                otherwise.translateContents(classGen, methodGen);
158                exit = il.append(NOP);
159            }
160    
161            // now that end is known set targets of exit gotos
162            Iterator exitGotos = exitHandles.iterator();
163            while (exitGotos.hasNext()) {
164                BranchHandle gotoExit = (BranchHandle)exitGotos.next();
165                gotoExit.setTarget(exit);
166            }
167        }
168    }