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: NodeSetType.java 468649 2006-10-28 07:00:55Z minchau $
020     */
021    
022    package org.apache.xalan.xsltc.compiler.util;
023    
024    import org.apache.bcel.generic.ALOAD;
025    import org.apache.bcel.generic.ASTORE;
026    import org.apache.bcel.generic.BranchHandle;
027    import org.apache.bcel.generic.ConstantPoolGen;
028    import org.apache.bcel.generic.GOTO;
029    import org.apache.bcel.generic.IFLT;
030    import org.apache.bcel.generic.INVOKEINTERFACE;
031    import org.apache.bcel.generic.INVOKESTATIC;
032    import org.apache.bcel.generic.Instruction;
033    import org.apache.bcel.generic.InstructionList;
034    import org.apache.bcel.generic.PUSH;
035    import org.apache.xalan.xsltc.compiler.Constants;
036    import org.apache.xalan.xsltc.compiler.FlowList;
037    
038    /**
039     * @author Jacek Ambroziak
040     * @author Santiago Pericas-Geertsen
041     */
042    public final class NodeSetType extends Type {
043        protected NodeSetType() {}
044    
045        public String toString() {
046            return "node-set";
047        }
048    
049        public boolean identicalTo(Type other) {
050            return this == other;
051        }
052    
053        public String toSignature() {
054            return NODE_ITERATOR_SIG;
055        }
056    
057        public org.apache.bcel.generic.Type toJCType() {
058            return new org.apache.bcel.generic.ObjectType(NODE_ITERATOR);
059        }
060    
061        /**
062         * Translates a node-set into an object of internal type
063         * <code>type</code>. The translation to int is undefined
064         * since node-sets are always converted to
065         * reals in arithmetic expressions.
066         *
067         * @see     org.apache.xalan.xsltc.compiler.util.Type#translateTo
068         */
069        public void translateTo(ClassGenerator classGen, MethodGenerator methodGen, 
070                                Type type) {
071            if (type == Type.String) {
072                translateTo(classGen, methodGen, (StringType) type);
073            }
074            else if (type == Type.Boolean) {
075                translateTo(classGen, methodGen, (BooleanType) type);
076            }
077            else if (type == Type.Real) {
078                translateTo(classGen, methodGen, (RealType) type);
079            }
080            else if (type == Type.Node) {
081                translateTo(classGen, methodGen, (NodeType) type);
082            }
083            else if (type == Type.Reference) {
084                translateTo(classGen, methodGen, (ReferenceType) type);
085            }
086            else if (type == Type.Object) {
087                translateTo(classGen, methodGen, (ObjectType) type);
088            }
089            else {
090                ErrorMsg err = new ErrorMsg(ErrorMsg.DATA_CONVERSION_ERR,
091                                            toString(), type.toString());
092                classGen.getParser().reportError(Constants.FATAL, err);
093            }
094        }
095    
096        /**
097         * Translates an external Java Class into an internal type.
098         * Expects the Java object on the stack, pushes the internal type
099         */
100        public void translateFrom(ClassGenerator classGen, 
101            MethodGenerator methodGen, Class clazz) 
102        {
103                    
104            InstructionList il = methodGen.getInstructionList();
105            ConstantPoolGen cpg = classGen.getConstantPool();
106            if (clazz.getName().equals("org.w3c.dom.NodeList")) {
107               // w3c NodeList is on the stack from the external Java function call.
108               // call BasisFunction to consume NodeList and leave Iterator on
109               //    the stack. 
110               il.append(classGen.loadTranslet());   // push translet onto stack
111               il.append(methodGen.loadDOM());       // push DOM onto stack
112               final int convert = cpg.addMethodref(BASIS_LIBRARY_CLASS,
113                                            "nodeList2Iterator",
114                                            "("             
115                                             + "Lorg/w3c/dom/NodeList;"
116                                             + TRANSLET_INTF_SIG 
117                                             + DOM_INTF_SIG 
118                                             + ")" + NODE_ITERATOR_SIG );
119               il.append(new INVOKESTATIC(convert));
120            }
121            else if (clazz.getName().equals("org.w3c.dom.Node")) {
122               // w3c Node is on the stack from the external Java function call.
123               // call BasisLibrary.node2Iterator() to consume Node and leave 
124               // Iterator on the stack. 
125               il.append(classGen.loadTranslet());   // push translet onto stack
126               il.append(methodGen.loadDOM());       // push DOM onto stack
127               final int convert = cpg.addMethodref(BASIS_LIBRARY_CLASS,
128                                            "node2Iterator",
129                                            "("             
130                                             + "Lorg/w3c/dom/Node;"
131                                             + TRANSLET_INTF_SIG 
132                                             + DOM_INTF_SIG 
133                                             + ")" + NODE_ITERATOR_SIG );
134               il.append(new INVOKESTATIC(convert));
135            }
136            else {
137                ErrorMsg err = new ErrorMsg(ErrorMsg.DATA_CONVERSION_ERR,
138                    toString(), clazz.getName());
139                classGen.getParser().reportError(Constants.FATAL, err);
140            } 
141        }
142    
143    
144        /**
145         * Translates a node-set into a synthesized boolean.
146         * The boolean value of a node-set is "true" if non-empty
147         * and "false" otherwise. Notice that the 
148         * function getFirstNode() is called in translateToDesynthesized().
149         *
150         * @see     org.apache.xalan.xsltc.compiler.util.Type#translateTo
151         */
152        public void translateTo(ClassGenerator classGen, MethodGenerator methodGen, 
153                                BooleanType type) {
154            final InstructionList il = methodGen.getInstructionList();
155            FlowList falsel = translateToDesynthesized(classGen, methodGen, type);
156            il.append(ICONST_1);
157            final BranchHandle truec = il.append(new GOTO(null));
158            falsel.backPatch(il.append(ICONST_0));
159            truec.setTarget(il.append(NOP));
160        }
161    
162        /**
163         * Translates a node-set into a string. The string value of a node-set is
164         * value of its first element.
165         *
166         * @see     org.apache.xalan.xsltc.compiler.util.Type#translateTo
167         */
168        public void translateTo(ClassGenerator classGen, MethodGenerator methodGen, 
169                                StringType type) {
170            final InstructionList il = methodGen.getInstructionList();
171            getFirstNode(classGen, methodGen);
172            il.append(DUP);
173            final BranchHandle falsec = il.append(new IFLT(null));
174            Type.Node.translateTo(classGen, methodGen, type);
175            final BranchHandle truec = il.append(new GOTO(null));
176            falsec.setTarget(il.append(POP));
177            il.append(new PUSH(classGen.getConstantPool(), ""));
178            truec.setTarget(il.append(NOP));
179        }
180    
181        /**
182         * Expects a node-set on the stack and pushes a real.
183         * First the node-set is converted to string, and from string to real.
184         *
185         * @see     org.apache.xalan.xsltc.compiler.util.Type#translateTo
186         */
187        public void translateTo(ClassGenerator classGen, MethodGenerator methodGen, 
188                                RealType type) {
189            translateTo(classGen, methodGen, Type.String);
190            Type.String.translateTo(classGen, methodGen, Type.Real);        
191        }
192    
193        /**
194         * Expects a node-set on the stack and pushes a node.
195         *
196         * @see     org.apache.xalan.xsltc.compiler.util.Type#translateTo
197         */
198        public void translateTo(ClassGenerator classGen, MethodGenerator methodGen, 
199                                NodeType type) {
200            getFirstNode(classGen, methodGen);
201        }
202    
203        /**
204         * Subsume node-set into ObjectType.
205         *
206         * @see     org.apache.xalan.xsltc.compiler.util.Type#translateTo
207         */
208        public void translateTo(ClassGenerator classGen, MethodGenerator methodGen, 
209                                ObjectType type) {
210                methodGen.getInstructionList().append(NOP); 
211        }
212    
213        /**
214         * Translates a node-set into a non-synthesized boolean. It does not 
215         * push a 0 or a 1 but instead returns branchhandle list to be appended 
216         * to the false list.
217         *
218         * @see     org.apache.xalan.xsltc.compiler.util.Type#translateToDesynthesized
219         */
220        public FlowList translateToDesynthesized(ClassGenerator classGen, 
221                                                 MethodGenerator methodGen, 
222                                                 BooleanType type) {
223            final InstructionList il = methodGen.getInstructionList();
224            getFirstNode(classGen, methodGen);
225            return new FlowList(il.append(new IFLT(null)));
226        }
227    
228        /**
229         * Expects a node-set on the stack and pushes a boxed node-set.
230         * Node sets are already boxed so the translation is just a NOP.
231         *
232         * @see     org.apache.xalan.xsltc.compiler.util.Type#translateTo
233         */
234        public void translateTo(ClassGenerator classGen, MethodGenerator methodGen, 
235                                ReferenceType type) {
236            methodGen.getInstructionList().append(NOP);
237        }
238    
239        /**
240         * Translates a node-set into the Java type denoted by <code>clazz</code>. 
241         * Expects a node-set on the stack and pushes an object of the appropriate
242         * type after coercion.
243         */
244        public void translateTo(ClassGenerator classGen, MethodGenerator methodGen, 
245                                Class clazz) {
246            final ConstantPoolGen cpg = classGen.getConstantPool();
247            final InstructionList il = methodGen.getInstructionList();
248            final String className = clazz.getName();
249    
250            il.append(methodGen.loadDOM());
251            il.append(SWAP);
252    
253            if (className.equals("org.w3c.dom.Node")) {
254                int index = cpg.addInterfaceMethodref(DOM_INTF,
255                                                      MAKE_NODE,
256                                                      MAKE_NODE_SIG2);
257                il.append(new INVOKEINTERFACE(index, 2));
258            }
259            else if (className.equals("org.w3c.dom.NodeList") || 
260                     className.equals("java.lang.Object")) {
261                int index = cpg.addInterfaceMethodref(DOM_INTF,
262                                                      MAKE_NODE_LIST,
263                                                      MAKE_NODE_LIST_SIG2);
264                il.append(new INVOKEINTERFACE(index, 2));
265            }
266            else if (className.equals("java.lang.String")) {
267                int next = cpg.addInterfaceMethodref(NODE_ITERATOR,
268                                                     "next", "()I");
269                int index = cpg.addInterfaceMethodref(DOM_INTF,
270                                                     GET_NODE_VALUE,
271                                                     "(I)"+STRING_SIG);
272    
273                // Get next node from the iterator
274                il.append(new INVOKEINTERFACE(next, 1));
275                // Get the node's string value (from the DOM)
276                il.append(new INVOKEINTERFACE(index, 2));
277                           
278            }
279            else {
280                ErrorMsg err = new ErrorMsg(ErrorMsg.DATA_CONVERSION_ERR,
281                                            toString(), className);
282                classGen.getParser().reportError(Constants.FATAL, err);
283            }
284        }
285        
286        /**
287         * Some type conversions require gettting the first node from the node-set.
288         * This function is defined to avoid code repetition.
289         */
290        private void getFirstNode(ClassGenerator classGen, MethodGenerator methodGen) {
291            final ConstantPoolGen cpg = classGen.getConstantPool();
292            final InstructionList il = methodGen.getInstructionList();
293            il.append(new INVOKEINTERFACE(cpg.addInterfaceMethodref(NODE_ITERATOR,
294                                                                    NEXT,
295                                                                    NEXT_SIG), 1));
296        }
297    
298        /**
299         * Translates an object of this type to its boxed representation.
300         */ 
301        public void translateBox(ClassGenerator classGen,
302                                 MethodGenerator methodGen) {
303            translateTo(classGen, methodGen, Type.Reference);
304        }
305    
306        /**
307         * Translates an object of this type to its unboxed representation.
308         */ 
309        public void translateUnBox(ClassGenerator classGen,
310                                   MethodGenerator methodGen) {
311            methodGen.getInstructionList().append(NOP);
312        }
313    
314        /**
315         * Returns the class name of an internal type's external representation.
316         */
317        public String getClassName() {
318            return(NODE_ITERATOR);
319        }
320    
321    
322        public Instruction LOAD(int slot) {
323            return new ALOAD(slot);
324        }
325            
326        public Instruction STORE(int slot) {
327            return new ASTORE(slot);
328        }
329    }