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 }