code: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements. See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership. The ASF licenses this file
   * to you 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$
   */
  
  package org.apache.xalan.xsltc.compiler;
  
  import org.apache.bcel.generic.BranchHandle;
  import org.apache.bcel.generic.ConstantPoolGen;
  import org.apache.bcel.generic.GOTO;
  import org.apache.bcel.generic.IFEQ;
  import org.apache.bcel.generic.IFGE;
  import org.apache.bcel.generic.IFGT;
  import org.apache.bcel.generic.ILOAD;
  import org.apache.bcel.generic.INVOKEINTERFACE;
  import org.apache.bcel.generic.INVOKEVIRTUAL;
  import org.apache.bcel.generic.ISTORE;
  import org.apache.bcel.generic.InstructionHandle;
  import org.apache.bcel.generic.InstructionList;
  import org.apache.bcel.generic.LocalVariableGen;
  import org.apache.bcel.generic.PUSH;
  import org.apache.xalan.xsltc.compiler.util.ClassGenerator;
  import org.apache.xalan.xsltc.compiler.util.ErrorMsg;
  import org.apache.xalan.xsltc.compiler.util.MethodGenerator;
  import org.apache.xalan.xsltc.compiler.util.NodeSetType;
  import org.apache.xalan.xsltc.compiler.util.StringType;
  import org.apache.xalan.xsltc.compiler.util.Type;
  import org.apache.xalan.xsltc.compiler.util.TypeCheckError;
  import org.apache.xalan.xsltc.compiler.util.Util;
  import org.apache.xml.dtm.Axis;
  import org.apache.xml.utils.XML11Char;
  
  /**
   * @author Morten Jorgensen
   * @author Santiago Pericas-Geertsen
   */
  final class Key extends TopLevelElement {
  
      /**
       * The name of this key as defined in xsl:key.
       */
      private QName _name;
  
      /**
       * The pattern to match starting at the root node.
       */
      private Pattern _match; 
  
      /**
       * The expression that generates the values for this key.
       */
      private Expression _use;
  
      /**
       * The type of the _use expression.
       */
      private Type _useType;
          
      /**
       * Parse the <xsl:key> element and attributes
       * @param parser A reference to the stylesheet parser
       */
      public void parseContents(Parser parser) {
  
  	// Get the required attributes and parser XPath expressions
          final String name = getAttribute("name");
          if (!XML11Char.isXML11ValidQName(name)){
              ErrorMsg err = new ErrorMsg(ErrorMsg.INVALID_QNAME_ERR, name, this);
              parser.reportError(Constants.ERROR, err);           
          }
          
          // Parse key name and add to symbol table
          _name = parser.getQNameIgnoreDefaultNs(name);
          getSymbolTable().addKey(_name, this);
          
  	_match = parser.parsePattern(this, "match", null);
  	_use = parser.parseExpression(this, "use", null);
  
          // Make sure required attribute(s) have been set
          if (_name == null) {
  	    reportError(this, parser, ErrorMsg.REQUIRED_ATTR_ERR, "name");
  	    return;
          }
          if (_match.isDummy()) {
  	    reportError(this, parser, ErrorMsg.REQUIRED_ATTR_ERR, "match");
  	    return;
          }
          if (_use.isDummy()) {
  	    reportError(this, parser, ErrorMsg.REQUIRED_ATTR_ERR, "use");
  	    return;
          }
      }
  
      /**
       * Returns a String-representation of this key's name
       * @return The key's name (from the <xsl:key> elements 'name' attribute).
       */
      public String getName() {
  	return _name.toString();
      }
  
      public Type typeCheck(SymbolTable stable) throws TypeCheckError {
  	// Type check match pattern
  	_match.typeCheck(stable);
  
  	// Cast node values to string values (except for nodesets)
  	_useType = _use.typeCheck(stable);
  	if (_useType instanceof StringType == false &&
  	    _useType instanceof NodeSetType == false) 
  	{
  	    _use = new CastExpr(_use, Type.String);
  	}
  
  	return Type.Void;
      }
  
      /**
       * This method is called if the "use" attribute of the key contains a
       * node set. In this case we must traverse all nodes in the set and
       * create one entry in this key's index for each node in the set.
       */
      public void traverseNodeSet(ClassGenerator classGen,
  				MethodGenerator methodGen,
  				int buildKeyIndex) {
  	final ConstantPoolGen cpg = classGen.getConstantPool();
  	final InstructionList il = methodGen.getInstructionList();
  
  	// DOM.getStringValueX(nodeIndex) => String
  	final int getNodeValue = cpg.addInterfaceMethodref(DOM_INTF,
  							   GET_NODE_VALUE,
  							   "(I)"+STRING_SIG);
  							   
  	final int getNodeIdent = cpg.addInterfaceMethodref(DOM_INTF,
  							   "getNodeIdent",
  							   "(I)"+NODE_SIG);	
  							   
  	// AbstractTranslet.SetKeyIndexDom(name, Dom) => void
  	final int keyDom = cpg.addMethodref(TRANSLET_CLASS,
  					 "setKeyIndexDom",
  					 "("+STRING_SIG+DOM_INTF_SIG+")V");				 
  						   					   
  
  	// This variable holds the id of the node we found with the "match"
  	// attribute of xsl:key. This is the id we store, with the value we
  	// get from the nodes we find here, in the index for this key.
  	final LocalVariableGen parentNode =
  	    methodGen.addLocalVariable("parentNode",
  				       Util.getJCRefType("I"),
  				       null, null);
  
  	// Get the 'parameter' from the stack and store it in a local var.
  	parentNode.setStart(il.append(new ISTORE(parentNode.getIndex())));	
  
  	// Save current node and current iterator on the stack
  	il.append(methodGen.loadCurrentNode());
  	il.append(methodGen.loadIterator());
  
  	// Overwrite current iterator with one that gives us only what we want
  	_use.translate(classGen, methodGen);
  	_use.startIterator(classGen, methodGen);
  	il.append(methodGen.storeIterator());
  
  	final BranchHandle nextNode = il.append(new GOTO(null));
  	final InstructionHandle loop = il.append(NOP);
  
  	// Prepare to call buildKeyIndex(String name, int node, String value);
  	il.append(classGen.loadTranslet());
  	il.append(new PUSH(cpg, _name.toString()));
  	parentNode.setEnd(il.append(new ILOAD(parentNode.getIndex())));
  
  	// Now get the node value and push it on the parameter stack
  	il.append(methodGen.loadDOM());
  	il.append(methodGen.loadCurrentNode());
  	il.append(new INVOKEINTERFACE(getNodeValue, 2));		
  
  	// Finally do the call to add an entry in the index for this key.
  	il.append(new INVOKEVIRTUAL(buildKeyIndex));
  	
  	il.append(classGen.loadTranslet());
  	il.append(new PUSH(cpg, getName()));
  	il.append(methodGen.loadDOM());
  	il.append(new INVOKEVIRTUAL(keyDom));
  
  	nextNode.setTarget(il.append(methodGen.loadIterator()));
  	il.append(methodGen.nextNode());	
  
  	il.append(DUP);
  	il.append(methodGen.storeCurrentNode());
  	il.append(new IFGE(loop)); // Go on to next matching node....
  
  	// Restore current node and current iterator from the stack
  	il.append(methodGen.storeIterator());
  	il.append(methodGen.storeCurrentNode());
      }
  
      /**
       * Gather all nodes that match the expression in the attribute "match"
       * and add one (or more) entries in this key's index.
       */
      public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
  
  	final ConstantPoolGen cpg = classGen.getConstantPool();
  	final InstructionList il = methodGen.getInstructionList();
  	final int current = methodGen.getLocalIndex("current");
  
  	// AbstractTranslet.buildKeyIndex(name,node_id,value) => void
  	final int key = cpg.addMethodref(TRANSLET_CLASS,
  					 "buildKeyIndex",
  					 "("+STRING_SIG+"I"+OBJECT_SIG+")V");
  					 
  	// AbstractTranslet.SetKeyIndexDom(name, Dom) => void
  	final int keyDom = cpg.addMethodref(TRANSLET_CLASS,
  					 "setKeyIndexDom",
  					 "("+STRING_SIG+DOM_INTF_SIG+")V");
  					 
  	final int getNodeIdent = cpg.addInterfaceMethodref(DOM_INTF,
  							   "getNodeIdent",
  							   "(I)"+NODE_SIG);					 				 
  
  	// DOM.getAxisIterator(root) => NodeIterator
  	final int git = cpg.addInterfaceMethodref(DOM_INTF,
  						  "getAxisIterator",
  						  "(I)"+NODE_ITERATOR_SIG);
  
  	il.append(methodGen.loadCurrentNode());
  	il.append(methodGen.loadIterator());
  
  	// Get an iterator for all nodes in the DOM
  	il.append(methodGen.loadDOM());	
  	il.append(new PUSH(cpg,Axis.DESCENDANT));
  	il.append(new INVOKEINTERFACE(git, 2));
  
  	// Reset the iterator to start with the root node
  	il.append(methodGen.loadCurrentNode());
  	il.append(methodGen.setStartNode());
  	il.append(methodGen.storeIterator());
  
  	// Loop for traversing all nodes in the DOM
  	final BranchHandle nextNode = il.append(new GOTO(null));
  	final InstructionHandle loop = il.append(NOP);
  
  	// Check if the current node matches the pattern in "match"
  	il.append(methodGen.loadCurrentNode());
  	_match.translate(classGen, methodGen);
  	_match.synthesize(classGen, methodGen); // Leaves 0 or 1 on stack
  	final BranchHandle skipNode = il.append(new IFEQ(null));
  	
  	// If this is a node-set we must go through each node in the set
  	if (_useType instanceof NodeSetType) {
  	    // Pass current node as parameter (we're indexing on that node)
  	    il.append(methodGen.loadCurrentNode());
  	    traverseNodeSet(classGen, methodGen, key);
  	}
  	else {
  	    il.append(classGen.loadTranslet());
  	    il.append(DUP);
  	    il.append(new PUSH(cpg, _name.toString()));
  	    il.append(DUP_X1);
  	    il.append(methodGen.loadCurrentNode());
  	    _use.translate(classGen, methodGen);
  	    il.append(new INVOKEVIRTUAL(key));
  	    
  	    il.append(methodGen.loadDOM());
  	    il.append(new INVOKEVIRTUAL(keyDom));
  	}
  	
  	// Get the next node from the iterator and do loop again...
  	final InstructionHandle skip = il.append(NOP);
  	
  	il.append(methodGen.loadIterator());
  	il.append(methodGen.nextNode());
  	il.append(DUP);
  	il.append(methodGen.storeCurrentNode());
  	il.append(new IFGT(loop));
  
  	// Restore current node and current iterator from the stack
  	il.append(methodGen.storeIterator());
  	il.append(methodGen.storeCurrentNode());
  	
  	nextNode.setTarget(skip);
  	skipNode.setTarget(skip);
      }
  }
  