FileDocCategorySizeDatePackage
KeyCall.javaAPI DocJava SE 5 API12068Fri Aug 26 14:55:36 BST 2005com.sun.org.apache.xalan.internal.xsltc.compiler

KeyCall

public final class KeyCall extends FunctionCall
author
Morten Jorgensen
author
Santiago Pericas-Geertsen

Fields Summary
private Expression
_name
The name of the key.
private Expression
_value
The value to look up in the key/index.
private Type
_valueType
The value's data type.
private QName
_resolvedQName
Expanded qname when name is literal.
Constructors Summary
public KeyCall(QName fname, Vector arguments)
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


                                                                                        
         
	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;
	}
    
Methods Summary
public voidtranslate(com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator classGen, com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator methodGen)
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

	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));
	
    
private voidtranslateCall(com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator classGen, com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator methodGen)
Translate the actual index lookup - leaves KeyIndex (iterator) on stack

param
classGen The Java class generator
param
methodGen The method generator


	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));
	    }
	}
    
public com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypetypeCheck(com.sun.org.apache.xalan.internal.xsltc.compiler.SymbolTable stable)
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

	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;