286N/A/*
286N/A * reserved comment block
286N/A * DO NOT REMOVE OR ALTER!
286N/A */
286N/A/*
286N/A * Copyright 2001-2006 The Apache Software Foundation.
286N/A *
286N/A * Licensed under the Apache License, Version 2.0 (the "License");
286N/A * you may not use this file except in compliance with the License.
286N/A * You may obtain a copy of the License at
286N/A *
286N/A * http://www.apache.org/licenses/LICENSE-2.0
286N/A *
286N/A * Unless required by applicable law or agreed to in writing, software
286N/A * distributed under the License is distributed on an "AS IS" BASIS,
286N/A * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
286N/A * See the License for the specific language governing permissions and
286N/A * limitations under the License.
286N/A */
286N/A/*
286N/A * $Id: KeyCall.java,v 1.7 2006/06/19 19:49:04 spericas Exp $
286N/A */
286N/A
286N/Apackage com.sun.org.apache.xalan.internal.xsltc.compiler;
286N/A
286N/Aimport java.util.Vector;
286N/A
286N/Aimport com.sun.org.apache.bcel.internal.generic.ALOAD;
286N/Aimport com.sun.org.apache.bcel.internal.generic.ASTORE;
286N/Aimport com.sun.org.apache.bcel.internal.generic.BranchHandle;
286N/Aimport com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
286N/Aimport com.sun.org.apache.bcel.internal.generic.GOTO;
286N/Aimport com.sun.org.apache.bcel.internal.generic.IFGT;
286N/Aimport com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE;
286N/Aimport com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL;
286N/Aimport com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
286N/Aimport com.sun.org.apache.bcel.internal.generic.InstructionHandle;
286N/Aimport com.sun.org.apache.bcel.internal.generic.InstructionList;
286N/Aimport com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
286N/Aimport com.sun.org.apache.bcel.internal.generic.NEW;
286N/Aimport com.sun.org.apache.bcel.internal.generic.PUSH;
286N/Aimport com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
286N/Aimport com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
286N/Aimport com.sun.org.apache.xalan.internal.xsltc.compiler.util.StringType;
286N/Aimport com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
286N/Aimport com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
286N/Aimport com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
286N/A
286N/A/**
286N/A * @author Morten Jorgensen
286N/A * @author Santiago Pericas-Geertsen
286N/A */
286N/Afinal class KeyCall extends FunctionCall {
286N/A
286N/A /**
286N/A * The name of the key.
286N/A */
286N/A private Expression _name;
286N/A
286N/A /**
286N/A * The value to look up in the key/index.
286N/A */
286N/A private Expression _value;
286N/A
286N/A /**
286N/A * The value's data type.
286N/A */
286N/A private Type _valueType; // The value's data type
286N/A
286N/A /**
286N/A * Expanded qname when name is literal.
286N/A */
286N/A private QName _resolvedQName = null;
286N/A
286N/A /**
286N/A * Get the parameters passed to function:
286N/A * key(String name, String value)
286N/A * key(String name, NodeSet value)
286N/A * The 'arguments' vector should contain two parameters for key() calls,
286N/A * one holding the key name and one holding the value(s) to look up. The
286N/A * vector has only one parameter for id() calls (the key name is always
286N/A * "##id" for id() calls).
286N/A *
286N/A * @param fname The function name (should be 'key' or 'id')
286N/A * @param arguments A vector containing the arguments the the function
286N/A */
286N/A public KeyCall(QName fname, Vector arguments) {
286N/A super(fname, arguments);
286N/A switch(argumentCount()) {
286N/A case 1:
286N/A _name = null;
286N/A _value = argument(0);
286N/A break;
286N/A case 2:
286N/A _name = argument(0);
286N/A _value = argument(1);
286N/A break;
286N/A default:
286N/A _name = _value = null;
286N/A break;
286N/A }
286N/A }
286N/A
286N/A /**
286N/A * If this call to key() is in a top-level element like another variable
286N/A * or param, add a dependency between that top-level element and the
286N/A * referenced key. For example,
286N/A *
286N/A * <xsl:key name="x" .../>
286N/A * <xsl:variable name="y" select="key('x', 1)"/>
286N/A *
286N/A * and assuming this class represents "key('x', 1)", add a reference
286N/A * between variable y and key x. Note that if 'x' is unknown statically
286N/A * in key('x', 1), there's nothing we can do at this point.
286N/A */
286N/A public void addParentDependency() {
286N/A // If name unknown statically, there's nothing we can do
286N/A if (_resolvedQName == null) return;
286N/A
286N/A SyntaxTreeNode node = this;
286N/A while (node != null && node instanceof TopLevelElement == false) {
286N/A node = node.getParent();
286N/A }
286N/A
286N/A TopLevelElement parent = (TopLevelElement) node;
286N/A if (parent != null) {
286N/A parent.addDependency(getSymbolTable().getKey(_resolvedQName));
286N/A }
286N/A }
286N/A
286N/A /**
286N/A * Type check the parameters for the id() or key() function.
286N/A * The index name (for key() call only) must be a string or convertable
286N/A * to a string, and the lookup-value must be a string or a node-set.
286N/A * @param stable The parser's symbol table
286N/A * @throws TypeCheckError When the parameters have illegal type
286N/A */
286N/A public Type typeCheck(SymbolTable stable) throws TypeCheckError {
286N/A final Type returnType = super.typeCheck(stable);
286N/A
286N/A // Run type check on the key name (first argument) - must be a string,
286N/A // and if it is not it must be converted to one using string() rules.
286N/A if (_name != null) {
286N/A final Type nameType = _name.typeCheck(stable);
286N/A
286N/A if (_name instanceof LiteralExpr) {
286N/A final LiteralExpr literal = (LiteralExpr) _name;
286N/A _resolvedQName =
286N/A getParser().getQNameIgnoreDefaultNs(literal.getValue());
286N/A }
286N/A else if (nameType instanceof StringType == false) {
286N/A _name = new CastExpr(_name, Type.String);
286N/A }
286N/A }
286N/A
286N/A // Run type check on the value for this key. This value can be of
286N/A // any data type, so this should never cause any type-check errors.
286N/A // If the value is a reference, then we have to defer the decision
286N/A // of how to process it until run-time.
286N/A // If the value is known not to be a node-set, then it should be
286N/A // converted to a string before the lookup is done. If the value is
286N/A // known to be a node-set then this process (convert to string, then
286N/A // do lookup) should be applied to every node in the set, and the
286N/A // result from all lookups should be added to the resulting node-set.
286N/A _valueType = _value.typeCheck(stable);
286N/A
286N/A if (_valueType != Type.NodeSet
286N/A && _valueType != Type.Reference
286N/A && _valueType != Type.String) {
286N/A _value = new CastExpr(_value, Type.String);
286N/A _valueType = _value.typeCheck(stable);
286N/A }
286N/A
286N/A // If in a top-level element, create dependency to the referenced key
286N/A addParentDependency();
286N/A
286N/A return returnType;
286N/A }
286N/A
286N/A /**
286N/A * This method is called when the constructor is compiled in
286N/A * Stylesheet.compileConstructor() and not as the syntax tree is traversed.
286N/A * <p>This method will generate byte code that produces an iterator
286N/A * for the nodes in the node set for the key or id function call.
286N/A * @param classGen The Java class generator
286N/A * @param methodGen The method generator
286N/A */
286N/A public void translate(ClassGenerator classGen,
286N/A MethodGenerator methodGen) {
286N/A final ConstantPoolGen cpg = classGen.getConstantPool();
286N/A final InstructionList il = methodGen.getInstructionList();
286N/A
286N/A // Returns the KeyIndex object of a given name
286N/A final int getKeyIndex = cpg.addMethodref(TRANSLET_CLASS,
286N/A "getKeyIndex",
286N/A "(Ljava/lang/String;)"+
286N/A KEY_INDEX_SIG);
286N/A
286N/A // KeyIndex.setDom(Dom, node) => void
286N/A final int keyDom = cpg.addMethodref(KEY_INDEX_CLASS,
286N/A "setDom",
286N/A "(" + DOM_INTF_SIG + "I)V");
286N/A
286N/A // Initialises a KeyIndex to return nodes with specific values
286N/A final int getKeyIterator =
286N/A cpg.addMethodref(KEY_INDEX_CLASS,
286N/A "getKeyIndexIterator",
286N/A "(" + _valueType.toSignature() + "Z)"
286N/A + KEY_INDEX_ITERATOR_SIG);
286N/A
286N/A // Initialise the index specified in the first parameter of key()
286N/A il.append(classGen.loadTranslet());
286N/A if (_name == null) {
286N/A il.append(new PUSH(cpg,"##id"));
286N/A } else if (_resolvedQName != null) {
286N/A il.append(new PUSH(cpg, _resolvedQName.toString()));
286N/A } else {
286N/A _name.translate(classGen, methodGen);
286N/A }
286N/A
286N/A // Generate following byte code:
286N/A //
286N/A // KeyIndex ki = translet.getKeyIndex(_name)
286N/A // ki.setDom(translet.dom);
286N/A // ki.getKeyIndexIterator(_value, true) - for key()
286N/A // OR
286N/A // ki.getKeyIndexIterator(_value, false) - for id()
286N/A il.append(new INVOKEVIRTUAL(getKeyIndex));
286N/A il.append(DUP);
286N/A il.append(methodGen.loadDOM());
286N/A il.append(methodGen.loadCurrentNode());
286N/A il.append(new INVOKEVIRTUAL(keyDom));
286N/A
286N/A _value.translate(classGen, methodGen);
286N/A il.append((_name != null) ? ICONST_1: ICONST_0);
286N/A il.append(new INVOKEVIRTUAL(getKeyIterator));
286N/A }
286N/A}