content: /*
   *  This file is part of AndroidIDE.
   *
   *  AndroidIDE is free software: you can redistribute it and/or modify
   *  it under the terms of the GNU General Public License as published by
   *  the Free Software Foundation, either version 3 of the License, or
   *  (at your option) any later version.
   *
   *  AndroidIDE is distributed in the hope that it will be useful,
   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *  GNU General Public License for more details.
   *
   *  You should have received a copy of the GNU General Public License
   *   along with AndroidIDE.  If not, see <https://www.gnu.org/licenses/>.
   */
  
  /*
   * reserved comment block
   * DO NOT REMOVE OR ALTER!
   */
  /*
   * 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: Predicate.java,v 1.2.4.1 2005/09/12 11:02:18 pvedula Exp $
   */
  
  package jaxp.sun.org.apache.xalan.internal.xsltc.compiler;
  
  import java.util.ArrayList;
  
  import jaxp.sun.org.apache.bcel.internal.classfile.Field;
  import jaxp.sun.org.apache.bcel.internal.generic.ASTORE;
  import jaxp.sun.org.apache.bcel.internal.generic.CHECKCAST;
  import jaxp.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
  import jaxp.sun.org.apache.bcel.internal.generic.GETFIELD;
  import jaxp.sun.org.apache.bcel.internal.generic.INVOKESPECIAL;
  import jaxp.sun.org.apache.bcel.internal.generic.InstructionList;
  import jaxp.sun.org.apache.bcel.internal.generic.LocalVariableGen;
  import jaxp.sun.org.apache.bcel.internal.generic.NEW;
  import jaxp.sun.org.apache.bcel.internal.generic.PUSH;
  import jaxp.sun.org.apache.bcel.internal.generic.PUTFIELD;
  import jaxp.sun.org.apache.xalan.internal.xsltc.compiler.util.BooleanType;
  import jaxp.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
  import jaxp.sun.org.apache.xalan.internal.xsltc.compiler.util.FilterGenerator;
  import jaxp.sun.org.apache.xalan.internal.xsltc.compiler.util.IntType;
  import jaxp.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
  import jaxp.sun.org.apache.xalan.internal.xsltc.compiler.util.NumberType;
  import jaxp.sun.org.apache.xalan.internal.xsltc.compiler.util.ReferenceType;
  import jaxp.sun.org.apache.xalan.internal.xsltc.compiler.util.ResultTreeType;
  import jaxp.sun.org.apache.xalan.internal.xsltc.compiler.util.TestGenerator;
  import jaxp.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
  import jaxp.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
  import jaxp.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
  import jaxp.sun.org.apache.xalan.internal.xsltc.runtime.Operators;
  
  /**
   * @author Jacek Ambroziak
   * @author Santiago Pericas-Geertsen
   * @author Morten Jorgensen
   */
  final class Predicate extends Expression implements Closure {
  
      /**
       * The predicate's expression.
       */
      private Expression _exp = null;
  
      /**
       * This flag indicates if optimizations are turned on. The
       * method <code>dontOptimize()</code> can be called to turn
       * optimizations off.
       */
      private boolean _canOptimize = true;
  
      /**
       * Flag indicatig if the nth position optimization is on. It
       * is set in <code>typeCheck()</code>.
       */
      private boolean _nthPositionFilter = false;
  
      /**
       * Flag indicatig if the nth position descendant is on. It
       * is set in <code>typeCheck()</code>.
       */
      private boolean _nthDescendant = false;
  
      /**
       * Cached node type of the expression that owns this predicate.
       */
      int _ptype = -1;
  
      /**
       * Name of the inner class.
       */
      private String _className = null;
  
      /**
       * List of variables in closure.
       */
      private ArrayList _closureVars = null;
  
      /**
       * Reference to parent closure.
       */
      private Closure _parentClosure = null;
  
      /**
       * Cached value of method <code>getCompareValue()</code>.
       */
      private Expression _value = null;
  
      /**
       * Cached value of method <code>getCompareValue()</code>.
       */
      private Step _step = null;
  
      /**
       * Initializes a predicate.
       */
      public Predicate(Expression exp) {
          _exp = exp;
          _exp.setParent(this);
  
      }
  
      /**
       * Set the parser for this expression.
       */
      public void setParser(Parser parser) {
          super.setParser(parser);
          _exp.setParser(parser);
      }
  
      /**
       * Returns a boolean value indicating if the nth position optimization
       * is on. Must be call after type checking!
       */
      public boolean isNthPositionFilter() {
          return _nthPositionFilter;
      }
  
      /**
       * Returns a boolean value indicating if the nth descendant optimization
       * is on. Must be call after type checking!
       */
      public boolean isNthDescendant() {
          return _nthDescendant;
      }
  
      /**
       * Turns off all optimizations for this predicate.
       */
      public void dontOptimize() {
          _canOptimize = false;
      }
  
      /**
       * Returns true if the expression in this predicate contains a call
       * to position().
       */
      public boolean hasPositionCall() {
          return _exp.hasPositionCall();
      }
  
      /**
       * Returns true if the expression in this predicate contains a call
       * to last().
       */
      public boolean hasLastCall() {
          return _exp.hasLastCall();
      }
  
      // -- Begin Closure interface --------------------
  
      /**
       * Returns true if this closure is compiled in an inner class (i.e.
       * if this is a real closure).
       */
      public boolean inInnerClass() {
          return (_className != null);
      }
  
      /**
       * Returns a reference to its parent closure or null if outermost.
       */
      public Closure getParentClosure() {
          if (_parentClosure == null) {
              SyntaxTreeNode node = getParent();
              do {
                  if (node instanceof Closure) {
                      _parentClosure = (Closure) node;
                      break;
                  }
                  if (node instanceof TopLevelElement) {
                      break;      // way up in the tree
                  }
                  node = node.getParent();
              } while (node != null);
          }
          return _parentClosure;
      }
  
      /**
       * Returns the name of the auxiliary class or null if this predicate
       * is compiled inside the Translet.
       */
      public String getInnerClassName() {
          return _className;
      }
  
      /**
       * Add new variable to the closure.
       */
      public void addVariable(VariableRefBase variableRef) {
          if (_closureVars == null) {
              _closureVars = new ArrayList();
          }
  
          // Only one reference per variable
          if (!_closureVars.contains(variableRef)) {
              _closureVars.add(variableRef);
  
              // Add variable to parent closure as well
              Closure parentClosure = getParentClosure();
              if (parentClosure != null) {
                  parentClosure.addVariable(variableRef);
              }
          }
      }
  
      // -- End Closure interface ----------------------
  
      /**
       * Returns the node type of the expression owning this predicate. The
       * return value is cached in <code>_ptype</code>.
       */
      public int getPosType() {
          if (_ptype == -1) {
              SyntaxTreeNode parent = getParent();
              if (parent instanceof StepPattern) {
                  _ptype = ((StepPattern)parent).getNodeType();
              }
              else if (parent instanceof AbsoluteLocationPath) {
                  AbsoluteLocationPath path = (AbsoluteLocationPath)parent;
                  Expression exp = path.getPath();
                  if (exp instanceof Step) {
                      _ptype = ((Step)exp).getNodeType();
                  }
              }
              else if (parent instanceof VariableRefBase) {
                  final VariableRefBase ref = (VariableRefBase)parent;
                  final VariableBase var = ref.getVariable();
                  final Expression exp = var.getExpression();
                  if (exp instanceof Step) {
                      _ptype = ((Step)exp).getNodeType();
                  }
              }
              else if (parent instanceof Step) {
                  _ptype = ((Step)parent).getNodeType();
              }
          }
          return _ptype;
      }
  
      public boolean parentIsPattern() {
          return (getParent() instanceof Pattern);
      }
  
      public Expression getExpr() {
          return _exp;
      }
  
      public String toString() {
          return "pred(" + _exp + ')';
      }
  
      /**
       * Type check a predicate expression. If the type of the expression is
       * number convert it to boolean by adding a comparison with position().
       * Note that if the expression is a parameter, we cannot distinguish
       * at compile time if its type is number or not. Hence, expressions of
       * reference type are always converted to booleans.
       *
       * This method may be called twice, before and after calling
       * <code>dontOptimize()</code>. If so, the second time it should honor
       * the new value of <code>_canOptimize</code>.
       */
      public Type typeCheck(SymbolTable stable) throws TypeCheckError {
          Type texp = _exp.typeCheck(stable);
  
          // We need explicit type information for reference types - no good!
          if (texp instanceof ReferenceType) {
              _exp = new CastExpr(_exp, texp = Type.Real);
          }
  
          // A result tree fragment should not be cast directly to a number type,
          // but rather to a boolean value, and then to a numer (0 or 1).
          // Ref. section 11.2 of the XSLT 1.0 spec
          if (texp instanceof ResultTreeType) {
              _exp = new CastExpr(_exp, Type.Boolean);
              _exp = new CastExpr(_exp, Type.Real);
              texp = _exp.typeCheck(stable);
          }
  
          // Numerical types will be converted to a position filter
          if (texp instanceof NumberType) {
              // Cast any numerical types to an integer
              if (texp instanceof IntType == false) {
                  _exp = new CastExpr(_exp, Type.Int);
              }
  
              if (_canOptimize) {
                  // Nth position optimization. Expression must not depend on context
                  _nthPositionFilter =
                      !_exp.hasLastCall() && !_exp.hasPositionCall();
  
                  // _nthDescendant optimization - only if _nthPositionFilter is on
                  if (_nthPositionFilter) {
                      SyntaxTreeNode parent = getParent();
                      _nthDescendant = (parent instanceof Step) &&
                          (parent.getParent() instanceof AbsoluteLocationPath);
                      return _type = Type.NodeSet;
                  }
              }
  
             // Reset optimization flags
              _nthPositionFilter = _nthDescendant = false;
  
             // Otherwise, expand [e] to [position() = e]
             final QName position =
                  getParser().getQNameIgnoreDefaultNs("position");
             final PositionCall positionCall =
                  new PositionCall(position);
             positionCall.setParser(getParser());
             positionCall.setParent(this);
  
             _exp = new EqualityExpr(Operators.EQ, positionCall,
                                      _exp);
             if (_exp.typeCheck(stable) != Type.Boolean) {
                 _exp = new CastExpr(_exp, Type.Boolean);
             }
             return _type = Type.Boolean;
          }
          else {
              // All other types will be handled as boolean values
              if (texp instanceof BooleanType == false) {
                  _exp = new CastExpr(_exp, Type.Boolean);
              }
              return _type = Type.Boolean;
          }
      }
  
      /**
       * Create a new "Filter" class implementing
       * <code>CurrentNodeListFilter</code>. Allocate registers for local
       * variables and local parameters passed in the closure to test().
       * Notice that local variables need to be "unboxed".
       */
      private void compileFilter(ClassGenerator classGen,
                                 MethodGenerator methodGen) {
          TestGenerator testGen;
          LocalVariableGen local;
          FilterGenerator filterGen;
  
          _className = getXSLTC().getHelperClassName();
          filterGen = new FilterGenerator(_className,
                                          "java.lang.Object",
                                          toString(),
                                          ACC_PUBLIC | ACC_SUPER,
                                          new String[] {
                                              CURRENT_NODE_LIST_FILTER
                                          },
                                          classGen.getStylesheet());
  
          final ConstantPoolGen cpg = filterGen.getConstantPool();
          final int length = (_closureVars == null) ? 0 : _closureVars.size();
  
          // Add a new instance variable for each var in closure
          for (int i = 0; i < length; i++) {
              VariableBase var = ((VariableRefBase) _closureVars.get(i)).getVariable();
  
              filterGen.addField(new Field(ACC_PUBLIC,
                                          cpg.addUtf8(var.getEscapedName()),
                                          cpg.addUtf8(var.getType().toSignature()),
                                          null, cpg.getConstantPool()));
          }
  
          final InstructionList il = new InstructionList();
          testGen = new TestGenerator(ACC_PUBLIC | ACC_FINAL,
                                      jaxp.sun.org.apache.bcel.internal.generic.Type.BOOLEAN,
                                      new jaxp.sun.org.apache.bcel.internal.generic.Type[] {
                                          jaxp.sun.org.apache.bcel.internal.generic.Type.INT,
                                          jaxp.sun.org.apache.bcel.internal.generic.Type.INT,
                                          jaxp.sun.org.apache.bcel.internal.generic.Type.INT,
                                          jaxp.sun.org.apache.bcel.internal.generic.Type.INT,
                                          Util.getJCRefType(TRANSLET_SIG),
                                          Util.getJCRefType(NODE_ITERATOR_SIG)
                                      },
                                      new String[] {
                                          "node",
                                          "position",
                                          "last",
                                          "current",
                                          "translet",
                                          "iterator"
                                      },
                                      "test", _className, il, cpg);
  
          // Store the dom in a local variable
          local = testGen.addLocalVariable("document",
                                           Util.getJCRefType(DOM_INTF_SIG),
                                           null, null);
          final String className = classGen.getClassName();
          il.append(filterGen.loadTranslet());
          il.append(new CHECKCAST(cpg.addClass(className)));
          il.append(new GETFIELD(cpg.addFieldref(className,
                                                 DOM_FIELD, DOM_INTF_SIG)));
          local.setStart(il.append(new ASTORE(local.getIndex())));
  
          // Store the dom index in the test generator
          testGen.setDomIndex(local.getIndex());
  
          _exp.translate(filterGen, testGen);
          il.append(IRETURN);
  
          filterGen.addEmptyConstructor(ACC_PUBLIC);
          filterGen.addMethod(testGen);
  
          getXSLTC().dumpClass(filterGen.getJavaClass());
      }
  
      /**
       * Returns true if the predicate is a test for the existance of an
       * element or attribute. All we have to do is to get the first node
       * from the step, check if it is there, and then return true/false.
       */
      public boolean isBooleanTest() {
          return (_exp instanceof BooleanExpr);
      }
  
      /**
       * Method to see if we can optimise the predicate by using a specialised
       * iterator for expressions like '/foo/bar[@attr = $var]', which are
       * very common in many stylesheets
       */
      public boolean isNodeValueTest() {
          if (!_canOptimize) return false;
          return (getStep() != null && getCompareValue() != null);
      }
  
     /**
       * Returns the step in an expression of the form 'step = value'.
       * Null is returned if the expression is not of the right form.
       * Optimization if off if null is returned.
       */
      public Step getStep() {
          // Returned cached value if called more than once
          if (_step != null) {
              return _step;
          }
  
          // Nothing to do if _exp is null
          if (_exp == null) {
              return null;
          }
  
          // Ignore if not equality expression
          if (_exp instanceof EqualityExpr) {
              EqualityExpr exp = (EqualityExpr)_exp;
              Expression left = exp.getLeft();
              Expression right = exp.getRight();
  
              // Unwrap and set _step if appropriate
              if (left instanceof CastExpr) {
                  left = ((CastExpr) left).getExpr();
              }
              if (left instanceof Step) {
                  _step = (Step) left;
              }
  
              // Unwrap and set _step if appropriate
              if (right instanceof CastExpr) {
                  right = ((CastExpr)right).getExpr();
              }
              if (right instanceof Step) {
                  _step = (Step)right;
              }
          }
          return _step;
      }
  
      /**
       * Returns the value in an expression of the form 'step = value'.
       * A value may be either a literal string or a variable whose
       * type is string. Optimization if off if null is returned.
       */
      public Expression getCompareValue() {
          // Returned cached value if called more than once
          if (_value != null) {
              return _value;
          }
  
          // Nothing to to do if _exp is null
          if (_exp == null) {
              return null;
          }
  
          // Ignore if not an equality expression
          if (_exp instanceof EqualityExpr) {
              EqualityExpr exp = (EqualityExpr) _exp;
              Expression left = exp.getLeft();
              Expression right = exp.getRight();
  
              // Return if left is literal string
              if (left instanceof LiteralExpr) {
                  _value = left;
                  return _value;
              }
              // Return if left is a variable reference of type string
              if (left instanceof VariableRefBase &&
                  left.getType() == Type.String)
              {
                  _value = left;
                  return _value;
              }
  
              // Return if right is literal string
              if (right instanceof LiteralExpr) {
                  _value = right;
                  return _value;
              }
              // Return if left is a variable reference whose type is string
              if (right instanceof VariableRefBase &&
                  right.getType() == Type.String)
              {
                  _value = right;
                  return _value;
              }
          }
          return null;
      }
  
      /**
       * Translate a predicate expression. This translation pushes
       * two references on the stack: a reference to a newly created
       * filter object and a reference to the predicate's closure.
       */
      public void translateFilter(ClassGenerator classGen,
                                  MethodGenerator methodGen)
      {
          final ConstantPoolGen cpg = classGen.getConstantPool();
          final InstructionList il = methodGen.getInstructionList();
  
          // Compile auxiliary class for filter
          compileFilter(classGen, methodGen);
  
          // Create new instance of filter
          il.append(new NEW(cpg.addClass(_className)));
          il.append(DUP);
          il.append(new INVOKESPECIAL(cpg.addMethodref(_className,
                                                       "<init>", "()V")));
  
          // Initialize closure variables
          final int length = (_closureVars == null) ? 0 : _closureVars.size();
  
          for (int i = 0; i < length; i++) {
              VariableRefBase varRef = (VariableRefBase) _closureVars.get(i);
              VariableBase var = varRef.getVariable();
              Type varType = var.getType();
  
              il.append(DUP);
  
              // Find nearest closure implemented as an inner class
              Closure variableClosure = _parentClosure;
              while (variableClosure != null) {
                  if (variableClosure.inInnerClass()) break;
                  variableClosure = variableClosure.getParentClosure();
              }
  
              // Use getfield if in an inner class
              if (variableClosure != null) {
                  il.append(ALOAD_0);
                  il.append(new GETFIELD(
                      cpg.addFieldref(variableClosure.getInnerClassName(),
                          var.getEscapedName(), varType.toSignature())));
              }
              else {
                  // Use a load of instruction if in translet class
                  il.append(var.loadInstruction());
              }
  
              // Store variable in new closure
              il.append(new PUTFIELD(
                      cpg.addFieldref(_className, var.getEscapedName(),
                          varType.toSignature())));
          }
      }
  
      /**
       * Translate a predicate expression. If non of the optimizations apply
       * then this translation pushes two references on the stack: a reference
       * to a newly created filter object and a reference to the predicate's
       * closure. See class <code>Step</code> for further details.
       */
      public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
  
          final ConstantPoolGen cpg = classGen.getConstantPool();
          final InstructionList il = methodGen.getInstructionList();
  
          if (_nthPositionFilter || _nthDescendant) {
              _exp.translate(classGen, methodGen);
          }
          else if (isNodeValueTest() && (getParent() instanceof Step)) {
              _value.translate(classGen, methodGen);
              il.append(new CHECKCAST(cpg.addClass(STRING_CLASS)));
              il.append(new PUSH(cpg, ((EqualityExpr)_exp).getOp()));
          }
          else {
              translateFilter(classGen, methodGen);
          }
      }
  }
  