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: CastExpr.java 468650 2006-10-28 07:03:30Z minchau $
020     */
021    
022    package org.apache.xalan.xsltc.compiler;
023    
024    import org.apache.bcel.generic.ConstantPoolGen;
025    import org.apache.bcel.generic.IF_ICMPNE;
026    import org.apache.bcel.generic.INVOKEINTERFACE;
027    import org.apache.bcel.generic.InstructionList;
028    import org.apache.bcel.generic.SIPUSH;
029    import org.apache.xalan.xsltc.compiler.util.BooleanType;
030    import org.apache.xalan.xsltc.compiler.util.ClassGenerator;
031    import org.apache.xalan.xsltc.compiler.util.ErrorMsg;
032    import org.apache.xalan.xsltc.compiler.util.MethodGenerator;
033    import org.apache.xalan.xsltc.compiler.util.MultiHashtable;
034    import org.apache.xalan.xsltc.compiler.util.NodeType;
035    import org.apache.xalan.xsltc.compiler.util.ResultTreeType;
036    import org.apache.xalan.xsltc.compiler.util.Type;
037    import org.apache.xalan.xsltc.compiler.util.TypeCheckError;
038    import org.apache.xml.dtm.Axis;
039    
040    /**
041     * @author Jacek Ambroziak
042     * @author Santiago Pericas-Geertsen
043     * @author Morten Jorgensen
044     * @author Erwin Bolwidt <ejb@klomp.org>
045     */
046    final class CastExpr extends Expression {
047        private final Expression _left;
048    
049        /**
050         * Legal conversions between internal types.
051         */
052        static private MultiHashtable InternalTypeMap = new MultiHashtable();
053    
054        static {
055            // Possible type conversions between internal types
056            InternalTypeMap.put(Type.Boolean, Type.Boolean);
057            InternalTypeMap.put(Type.Boolean, Type.Real);
058            InternalTypeMap.put(Type.Boolean, Type.String);
059            InternalTypeMap.put(Type.Boolean, Type.Reference);
060            InternalTypeMap.put(Type.Boolean, Type.Object);
061    
062            InternalTypeMap.put(Type.Real, Type.Real);
063            InternalTypeMap.put(Type.Real, Type.Int);
064            InternalTypeMap.put(Type.Real, Type.Boolean);
065            InternalTypeMap.put(Type.Real, Type.String);
066            InternalTypeMap.put(Type.Real, Type.Reference);
067            InternalTypeMap.put(Type.Real, Type.Object);
068    
069            InternalTypeMap.put(Type.Int, Type.Int);
070            InternalTypeMap.put(Type.Int, Type.Real);
071            InternalTypeMap.put(Type.Int, Type.Boolean);
072            InternalTypeMap.put(Type.Int, Type.String);
073            InternalTypeMap.put(Type.Int, Type.Reference);
074            InternalTypeMap.put(Type.Int, Type.Object);
075    
076            InternalTypeMap.put(Type.String, Type.String);
077            InternalTypeMap.put(Type.String, Type.Boolean);
078            InternalTypeMap.put(Type.String, Type.Real);
079            InternalTypeMap.put(Type.String, Type.Reference);
080            InternalTypeMap.put(Type.String, Type.Object);
081    
082            InternalTypeMap.put(Type.NodeSet, Type.NodeSet);
083            InternalTypeMap.put(Type.NodeSet, Type.Boolean);
084            InternalTypeMap.put(Type.NodeSet, Type.Real);
085            InternalTypeMap.put(Type.NodeSet, Type.String);
086            InternalTypeMap.put(Type.NodeSet, Type.Node);
087            InternalTypeMap.put(Type.NodeSet, Type.Reference);
088            InternalTypeMap.put(Type.NodeSet, Type.Object);
089    
090            InternalTypeMap.put(Type.Node, Type.Node);
091            InternalTypeMap.put(Type.Node, Type.Boolean);
092            InternalTypeMap.put(Type.Node, Type.Real);
093            InternalTypeMap.put(Type.Node, Type.String);
094            InternalTypeMap.put(Type.Node, Type.NodeSet);
095            InternalTypeMap.put(Type.Node, Type.Reference);
096            InternalTypeMap.put(Type.Node, Type.Object);
097    
098            InternalTypeMap.put(Type.ResultTree, Type.ResultTree);
099            InternalTypeMap.put(Type.ResultTree, Type.Boolean);
100            InternalTypeMap.put(Type.ResultTree, Type.Real);
101            InternalTypeMap.put(Type.ResultTree, Type.String);
102            InternalTypeMap.put(Type.ResultTree, Type.NodeSet);
103            InternalTypeMap.put(Type.ResultTree, Type.Reference);
104            InternalTypeMap.put(Type.ResultTree, Type.Object);
105    
106            InternalTypeMap.put(Type.Reference, Type.Reference);
107            InternalTypeMap.put(Type.Reference, Type.Boolean);
108            InternalTypeMap.put(Type.Reference, Type.Int);
109            InternalTypeMap.put(Type.Reference, Type.Real);
110            InternalTypeMap.put(Type.Reference, Type.String);
111            InternalTypeMap.put(Type.Reference, Type.Node);
112            InternalTypeMap.put(Type.Reference, Type.NodeSet);
113            InternalTypeMap.put(Type.Reference, Type.ResultTree);
114            InternalTypeMap.put(Type.Reference, Type.Object);
115    
116            InternalTypeMap.put(Type.Object, Type.String);
117    
118            InternalTypeMap.put(Type.Void, Type.String);
119        }
120    
121        private boolean _typeTest = false;
122    
123        /**
124         * Construct a cast expression and check that the conversion is 
125         * valid by calling typeCheck().
126         */
127        public CastExpr(Expression left, Type type) throws TypeCheckError {
128            _left = left;
129            _type = type;           // use inherited field
130    
131            if ((_left instanceof Step) && (_type == Type.Boolean)) {
132                Step step = (Step)_left;
133                if ((step.getAxis() == Axis.SELF) && (step.getNodeType() != -1)) 
134                    _typeTest = true;
135            }
136            
137            // check if conversion is valid
138            setParser(left.getParser());
139            setParent(left.getParent());
140            left.setParent(this);
141            typeCheck(left.getParser().getSymbolTable());
142        }
143                    
144        public Expression getExpr() {
145            return _left;
146        }
147    
148        /**
149         * Returns true if this expressions contains a call to position(). This is
150         * needed for context changes in node steps containing multiple predicates.
151         */
152        public boolean hasPositionCall() {
153            return(_left.hasPositionCall());
154        }
155    
156        public boolean hasLastCall() {
157            return(_left.hasLastCall());
158        }
159    
160        public String toString() {
161            return "cast(" + _left + ", " + _type + ")";
162        }
163    
164        /**
165         * Type checking a cast expression amounts to verifying that the  
166         * type conversion is legal. Cast expressions are created during 
167         * type checking, but typeCheck() is usually not called on them. 
168         * As a result, this method is called from the constructor.
169         */ 
170        public Type typeCheck(SymbolTable stable) throws TypeCheckError {
171            Type tleft = _left.getType();
172            if (tleft == null) {
173                tleft = _left.typeCheck(stable);
174            }
175            if (tleft instanceof NodeType) {
176                tleft = Type.Node;  // multiple instances
177            }
178            else if (tleft instanceof ResultTreeType) {
179                tleft = Type.ResultTree; // multiple instances
180            }
181            if (InternalTypeMap.maps(tleft, _type) != null) {
182                return _type;
183            }
184            // throw new TypeCheckError(this);      
185            throw new TypeCheckError(new ErrorMsg(
186                ErrorMsg.DATA_CONVERSION_ERR, tleft.toString(), _type.toString()));
187        }
188    
189        public void translateDesynthesized(ClassGenerator classGen, 
190                                           MethodGenerator methodGen) {
191            FlowList fl;
192            final Type ltype = _left.getType();
193    
194            // This is a special case for the self:: axis. Instead of letting
195            // the Step object create and iterator that we cast back to a single
196            // node, we simply ask the DOM for the node type.
197            if (_typeTest) {
198                final ConstantPoolGen cpg = classGen.getConstantPool();
199                final InstructionList il = methodGen.getInstructionList();
200    
201                final int idx = cpg.addInterfaceMethodref(DOM_INTF,
202                                                          "getExpandedTypeID",
203                                                          "(I)I");
204                il.append(new SIPUSH((short)((Step)_left).getNodeType()));
205                il.append(methodGen.loadDOM());
206                il.append(methodGen.loadContextNode());
207                il.append(new INVOKEINTERFACE(idx, 2));
208                _falseList.add(il.append(new IF_ICMPNE(null)));
209            }
210            else {
211    
212                _left.translate(classGen, methodGen);
213                if (_type != ltype) {
214                    _left.startIterator(classGen, methodGen);
215                    if (_type instanceof BooleanType) {
216                        fl = ltype.translateToDesynthesized(classGen, methodGen,
217                                                            _type);
218                        if (fl != null) {
219                            _falseList.append(fl);
220                        }
221                    }
222                    else {
223                        ltype.translateTo(classGen, methodGen, _type);      
224                    }
225                }
226            }
227        }
228    
229        public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
230            final Type ltype = _left.getType();
231            _left.translate(classGen, methodGen);
232            if (_type.identicalTo(ltype) == false) {
233                _left.startIterator(classGen, methodGen);
234                ltype.translateTo(classGen, methodGen, _type);
235            }
236        }
237    }