- /*
- * Copyright 2001-2004 The Apache Software Foundation.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- /*
- * $Id: KeyCall.java,v 1.15 2004/02/16 22:24:29 minchau Exp $
- */
-
- package com.sun.org.apache.xalan.internal.xsltc.compiler;
-
- import java.util.Vector;
-
- import com.sun.org.apache.bcel.internal.generic.ALOAD;
- import com.sun.org.apache.bcel.internal.generic.ASTORE;
- import com.sun.org.apache.bcel.internal.generic.BranchHandle;
- import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
- import com.sun.org.apache.bcel.internal.generic.GOTO;
- import com.sun.org.apache.bcel.internal.generic.IFGT;
- import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE;
- import com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL;
- import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
- import com.sun.org.apache.bcel.internal.generic.InstructionHandle;
- import com.sun.org.apache.bcel.internal.generic.InstructionList;
- import com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
- import com.sun.org.apache.bcel.internal.generic.NEW;
- import com.sun.org.apache.bcel.internal.generic.PUSH;
- import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
- import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
- import com.sun.org.apache.xalan.internal.xsltc.compiler.util.StringType;
- import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
- import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
- import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
-
- /**
- * @author Morten Jorgensen
- * @author Santiago Pericas-Geertsen
- */
- final class KeyCall extends FunctionCall {
-
- /**
- * The name of the key.
- */
- private Expression _name;
-
- /**
- * The value to look up in the key/index.
- */
- private Expression _value;
-
- /**
- * The value's data type.
- */
- private Type _valueType; // The value's data type
-
- /**
- * Expanded qname when name is literal.
- */
- private QName _resolvedQName = null;
-
- /**
- * Get the parameters passed to function:
- * key(String name, String value)
- * key(String name, NodeSet value)
- * The 'arguments' vector should contain two parameters for key() calls,
- * one holding the key name and one holding the value(s) to look up. The
- * vector has only one parameter for id() calls (the key name is always
- * "##id" for id() calls).
- *
- * @param fname The function name (should be 'key' or 'id')
- * @param arguments A vector containing the arguments the the function
- */
- public KeyCall(QName fname, Vector arguments) {
- super(fname, arguments);
- switch(argumentCount()) {
- case 1:
- _name = null;
- _value = argument(0);
- break;
- case 2:
- _name = argument(0);
- _value = argument(1);
- break;
- default:
- _name = _value = null;
- break;
- }
- }
-
- /**
- * Type check the parameters for the id() or key() function.
- * The index name (for key() call only) must be a string or convertable
- * to a string, and the lookup-value must be a string or a node-set.
- * @param stable The parser's symbol table
- * @throws TypeCheckError When the parameters have illegal type
- */
- public Type typeCheck(SymbolTable stable) throws TypeCheckError {
- final Type returnType = super.typeCheck(stable);
-
- // Run type check on the key name (first argument) - must be a string,
- // and if it is not it must be converted to one using string() rules.
- if (_name != null) {
- final Type nameType = _name.typeCheck(stable);
-
- if (_name instanceof LiteralExpr) {
- final LiteralExpr literal = (LiteralExpr) _name;
- _resolvedQName =
- getParser().getQNameIgnoreDefaultNs(literal.getValue());
- }
- else if (nameType instanceof StringType == false) {
- _name = new CastExpr(_name, Type.String);
- }
- }
-
- // Run type check on the value for this key. This value can be of
- // any data type, so this should never cause any type-check errors.
- // If the value is not a node-set then it should be converted to a
- // string before the lookup is done. If the value is a node-set then
- // this process (convert to string, then do lookup) should be applied
- // to every node in the set, and the result from all lookups should
- // be added to the resulting node-set.
- _valueType = _value.typeCheck(stable);
-
- if (_valueType != Type.NodeSet && _valueType != Type.String)
- {
- _value = new CastExpr(_value, Type.String);
- }
-
- return returnType;
- }
-
- /**
- * This method is called when the constructor is compiled in
- * Stylesheet.compileConstructor() and not as the syntax tree is traversed.
- * This method is a wrapper for the real translation method, which is
- * the private method translateCall() below. All this method does is to
- * wrap the KeyIndex that this function returns inside a duplicate filter.
- * The duplicate filter is used both to eliminate duplicates and to
- * cache the nodes in the index.
- * @param classGen The Java class generator
- * @param methodGen The method generator
- */
- public void translate(ClassGenerator classGen,
- MethodGenerator methodGen) {
- final ConstantPoolGen cpg = classGen.getConstantPool();
- final InstructionList il = methodGen.getInstructionList();
-
- final int getNodeHandle = cpg.addInterfaceMethodref(DOM_INTF,
- "getNodeHandle",
- "(I)"+NODE_SIG);
-
- // Wrap the KeyIndex (iterator) inside a duplicate filter iterator
- // to pre-read the indexed nodes and cache them.
- final int dupInit = cpg.addMethodref(DUP_FILTERED_ITERATOR,
- "<init>",
- "("+NODE_ITERATOR_SIG+")V");
-
- il.append(new NEW(cpg.addClass(DUP_FILTERED_ITERATOR)));
- il.append(DUP);
- translateCall(classGen, methodGen);
- il.append(new INVOKESPECIAL(dupInit));
-
- }
-
- /**
- * Translate the actual index lookup - leaves KeyIndex (iterator) on stack
- * @param classGen The Java class generator
- * @param methodGen The method generator
- */
- private void translateCall(ClassGenerator classGen,
- MethodGenerator methodGen) {
-
- final ConstantPoolGen cpg = classGen.getConstantPool();
- final InstructionList il = methodGen.getInstructionList();
-
- // Returns the string value for a node in the DOM
- final int getNodeValue = cpg.addInterfaceMethodref(DOM_INTF,
- GET_NODE_VALUE,
- "(I)"+STRING_SIG);
-
- // Returns the KeyIndex object of a given name
- final int getKeyIndex = cpg.addMethodref(TRANSLET_CLASS,
- "getKeyIndex",
- "(Ljava/lang/String;)"+
- KEY_INDEX_SIG);
-
- // Initialises a KeyIndex to return nodes with specific values
- final int lookupId = cpg.addMethodref(KEY_INDEX_CLASS,
- "lookupId",
- "(Ljava/lang/Object;)V");
- final int lookupKey = cpg.addMethodref(KEY_INDEX_CLASS,
- "lookupKey",
- "(Ljava/lang/Object;)V");
-
- // Merges the nodes in two KeyIndex objects
- final int merge = cpg.addMethodref(KEY_INDEX_CLASS,
- "merge",
- "("+KEY_INDEX_SIG+")V");
-
- // Constructor for KeyIndex class
- final int indexConstructor = cpg.addMethodref(TRANSLET_CLASS,
- "createKeyIndex",
- "()"+KEY_INDEX_SIG);
-
- // KeyIndex.setDom(Dom) => void
- final int keyDom = cpg.addMethodref(XSLT_PACKAGE + ".dom.KeyIndex",
- "setDom",
- "("+DOM_INTF_SIG+")V");
-
-
- // This local variable holds the index/iterator we will return
- final LocalVariableGen returnIndex =
- methodGen.addLocalVariable("returnIndex",
- Util.getJCRefType(KEY_INDEX_SIG),
- il.getEnd(), null);
-
- // This local variable holds the index we're using for search
- final LocalVariableGen searchIndex =
- methodGen.addLocalVariable("searchIndex",
- Util.getJCRefType(KEY_INDEX_SIG),
- il.getEnd(), null);
-
- // If the second paramter is a node-set we need to go through each
- // node in the set, convert each one to a string and do a look up in
- // the named index, and then merge all the resulting node sets.
- if (_valueType == Type.NodeSet) {
- // Save current node and current iterator on the stack
- il.append(methodGen.loadCurrentNode());
- il.append(methodGen.loadIterator());
-
- // Get new iterator from 2nd parameter node-set & store in variable
- _value.translate(classGen, methodGen);
- _value.startIterator(classGen, methodGen);
- il.append(methodGen.storeIterator());
-
- // Create the KeyIndex object (the iterator) we'll return
- il.append(classGen.loadTranslet());
- il.append(new INVOKEVIRTUAL(indexConstructor));
- il.append(DUP);
- il.append(methodGen.loadDOM());
- il.append(new INVOKEVIRTUAL(keyDom));
- il.append(new ASTORE(returnIndex.getIndex()));
-
- // Initialise the index specified in the first parameter of key()
- il.append(classGen.loadTranslet());
- if (_name == null) {
- il.append(new PUSH(cpg,"##id"));
- }
- else if (_resolvedQName != null) {
- il.append(new PUSH(cpg, _resolvedQName.toString()));
- }
- else {
- _name.translate(classGen, methodGen);
- }
-
- il.append(new INVOKEVIRTUAL(getKeyIndex));
- il.append(new ASTORE(searchIndex.getIndex()));
-
- // LOOP STARTS HERE
-
- // Now we're ready to start traversing the node-set given in
- // the key() function's second argument....
- final BranchHandle nextNode = il.append(new GOTO(null));
- final InstructionHandle loop = il.append(NOP);
-
- // Push returnIndex on stack to prepare for call to merge()
- il.append(new ALOAD(returnIndex.getIndex()));
-
- // Lookup index using the string value from the current node
- il.append(new ALOAD(searchIndex.getIndex()));
- il.append(DUP);
- il.append(methodGen.loadDOM());
- il.append(methodGen.loadCurrentNode());
- il.append(new INVOKEINTERFACE(getNodeValue, 2));
- if (_name == null) {
- il.append(new INVOKEVIRTUAL(lookupId));
- }
- else {
- il.append(new INVOKEVIRTUAL(lookupKey));
- }
-
- // Call to returnIndex.merge(searchIndex);
- il.append(new INVOKEVIRTUAL(merge));
-
- // Go on with next node in the 2nd parameter node-set
- nextNode.setTarget(il.append(methodGen.loadIterator()));
- il.append(methodGen.nextNode());
- il.append(DUP);
- il.append(methodGen.storeCurrentNode());
- il.append(new IFGT(loop));
-
- // LOOP ENDS HERE
-
- // Restore current node and current iterator from the stack
- il.append(methodGen.storeIterator());
- il.append(methodGen.storeCurrentNode());
-
- // Return with the an iterator for all resulting nodes
- il.append(new ALOAD(returnIndex.getIndex()));
- }
- // If the second parameter is a single value we just lookup the named
- // index and initialise the iterator to return nodes with this value.
- else {
- // Call getKeyIndex in AbstractTranslet with the name of the key
- // to get the index for this key (which is also a node iterator).
- il.append(classGen.loadTranslet());
- if (_name == null) {
- il.append(new PUSH(cpg,"##id"));
- }
- else if (_resolvedQName != null) {
- il.append(new PUSH(cpg, _resolvedQName.toString()));
- }
- else {
- _name.translate(classGen, methodGen);
- }
- il.append(new INVOKEVIRTUAL(getKeyIndex));
-
- // Now use the value in the second argument to determine what nodes
- // the iterator should return.
- il.append(DUP);
-
- _value.translate(classGen, methodGen);
-
- if (_name == null) {
- il.append(new INVOKEVIRTUAL(lookupId));
- }
- else {
- il.append(new INVOKEVIRTUAL(lookupKey));
- }
- }
- }
- }