1/*
2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
3 */
4/*
5 * Licensed to the Apache Software Foundation (ASF) under one or more
6 * contributor license agreements.  See the NOTICE file distributed with
7 * this work for additional information regarding copyright ownership.
8 * The ASF licenses this file to You under the Apache License, Version 2.0
9 * (the "License"); you may not use this file except in compliance with
10 * the License.  You may obtain a copy of the License at
11 *
12 *     http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20/*
21 * $Id: Choose.java,v 1.2.4.1 2005/09/01 12:00:14 pvedula Exp $
22 */
23
24package com.sun.org.apache.xalan.internal.xsltc.compiler;
25
26import java.util.Enumeration;
27import java.util.Vector;
28
29import com.sun.org.apache.bcel.internal.generic.BranchHandle;
30import com.sun.org.apache.bcel.internal.generic.GOTO;
31import com.sun.org.apache.bcel.internal.generic.IFEQ;
32import com.sun.org.apache.bcel.internal.generic.InstructionHandle;
33import com.sun.org.apache.bcel.internal.generic.InstructionList;
34import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
35import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
36import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
37import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
38import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
39import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
40import java.util.Iterator;
41
42/**
43 * @author Jacek Ambroziak
44 * @author Santiago Pericas-Geertsen
45 * @author Morten Jorgensen
46 */
47final class Choose extends Instruction {
48
49    /**
50     * Display the element contents (a lot of when's and an otherwise)
51     */
52    public void display(int indent) {
53        indent(indent);
54        Util.println("Choose");
55        indent(indent + IndentIncrement);
56        displayContents(indent + IndentIncrement);
57    }
58
59    /**
60     * Translate this Choose element. Generate a test-chain for the various
61     * <xsl:when> elements and default to the <xsl:otherwise> if present.
62     */
63    public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
64        final Vector whenElements = new Vector();
65        Otherwise otherwise = null;
66        Iterator<SyntaxTreeNode> elements = elements();
67
68        // These two are for reporting errors only
69        ErrorMsg error = null;
70        final int line = getLineNumber();
71
72        // Traverse all child nodes - must be either When or Otherwise
73        while (elements.hasNext()) {
74            SyntaxTreeNode element = elements.next();
75            // Add a When child element
76            if (element instanceof When) {
77                whenElements.addElement(element);
78            }
79            // Add an Otherwise child element
80            else if (element instanceof Otherwise) {
81                if (otherwise == null) {
82                    otherwise = (Otherwise)element;
83                }
84                else {
85                    error = new ErrorMsg(ErrorMsg.MULTIPLE_OTHERWISE_ERR, this);
86                    getParser().reportError(Constants.ERROR, error);
87                }
88            }
89            else if (element instanceof Text) {
90                ((Text)element).ignore();
91            }
92            // It is an error if we find some other element here
93            else {
94                error = new ErrorMsg(ErrorMsg.WHEN_ELEMENT_ERR, this);
95                getParser().reportError(Constants.ERROR, error);
96            }
97        }
98
99        // Make sure that there is at least one <xsl:when> element
100        if (whenElements.size() == 0) {
101            error = new ErrorMsg(ErrorMsg.MISSING_WHEN_ERR, this);
102            getParser().reportError(Constants.ERROR, error);
103            return;
104        }
105
106        InstructionList il = methodGen.getInstructionList();
107
108        // next element will hold a handle to the beginning of next
109        // When/Otherwise if test on current When fails
110        BranchHandle nextElement = null;
111        Vector exitHandles = new Vector();
112        InstructionHandle exit = null;
113
114        Enumeration whens = whenElements.elements();
115        while (whens.hasMoreElements()) {
116            final When when = (When)whens.nextElement();
117            final Expression test = when.getTest();
118
119            InstructionHandle truec = il.getEnd();
120
121            if (nextElement != null)
122                nextElement.setTarget(il.append(NOP));
123            test.translateDesynthesized(classGen, methodGen);
124
125            if (test instanceof FunctionCall) {
126                FunctionCall call = (FunctionCall)test;
127                try {
128                    Type type = call.typeCheck(getParser().getSymbolTable());
129                    if (type != Type.Boolean) {
130                        test._falseList.add(il.append(new IFEQ(null)));
131                    }
132                }
133                catch (TypeCheckError e) {
134                    // handled later!
135                }
136            }
137            // remember end of condition
138            truec = il.getEnd();
139
140            // The When object should be ignored completely in case it tests
141            // for the support of a non-available element
142            if (!when.ignore()) when.translateContents(classGen, methodGen);
143
144            // goto exit after executing the body of when
145            exitHandles.addElement(il.append(new GOTO(null)));
146            if (whens.hasMoreElements() || otherwise != null) {
147                nextElement = il.append(new GOTO(null));
148                test.backPatchFalseList(nextElement);
149            }
150            else
151                test.backPatchFalseList(exit = il.append(NOP));
152            test.backPatchTrueList(truec.getNext());
153        }
154
155        // Translate any <xsl:otherwise> element
156        if (otherwise != null) {
157            nextElement.setTarget(il.append(NOP));
158            otherwise.translateContents(classGen, methodGen);
159            exit = il.append(NOP);
160        }
161
162        // now that end is known set targets of exit gotos
163        Enumeration exitGotos = exitHandles.elements();
164        while (exitGotos.hasMoreElements()) {
165            BranchHandle gotoExit = (BranchHandle)exitGotos.nextElement();
166            gotoExit.setTarget(exit);
167        }
168    }
169}
170