/**
   * Creates a transformed copy of this expression, applying transformation provided by Transformer
   * to all its nodes. Null transformer will result in an identical deep copy of this expression.
   *
   * <p>To force a node and its children to be pruned from the copy, Transformer should return null
   * for a given node.
   *
   * <p>There is one limitation on what Transformer is expected to do: if a node is an Expression it
   * must be transformed to null or another Expression. Any other object type would result in an
   * exception.
   *
   * @since 1.1
   */
  public Expression transform(Transformer transformer) {

    Expression copy = shallowCopy();
    int count = getOperandCount();
    for (int i = 0, j = 0; i < count; i++) {
      Object operand = getOperand(i);
      Object transformedChild = operand;

      if (operand instanceof Expression) {
        transformedChild = ((Expression) operand).transform(transformer);
      } else if (transformer != null) {
        transformedChild = transformer.transform(operand);
      }

      if (transformedChild != null) {
        Object value = (transformedChild != nullValue) ? transformedChild : null;
        copy.setOperand(j, value);
        j++;
      } else if (pruneNodeForPrunedChild(operand)) {
        // bail out early...
        return null;
      }
    }

    // all the children are processed, only now transform this copy
    return (transformer != null) ? (Expression) transformer.transform(copy) : copy;
  }
 /**
  * Creates a new expression that joins this object with another expression, using specified join
  * type. It is very useful for incrementally building chained expressions, like long AND or OR
  * statements.
  */
 public Expression joinExp(int type, Expression exp) {
   Expression join = ExpressionFactory.expressionOfType(type);
   join.setOperand(0, this);
   join.setOperand(1, exp);
   join.flattenTree();
   return join;
 }
  /**
   * Traverses itself and child expressions, notifying visitor via callback methods as it goes.
   *
   * @since 1.1
   */
  protected void traverse(Expression parentExp, TraversalHandler visitor) {

    visitor.startNode(this, parentExp);

    // recursively traverse each child
    int count = getOperandCount();
    for (int i = 0; i < count; i++) {
      Object child = getOperand(i);

      if (child instanceof Expression) {
        Expression childExp = (Expression) child;
        childExp.traverse(this, visitor);
      } else {
        visitor.objectNode(child, this);
      }

      visitor.finishedChild(this, i, i < count - 1);
    }

    visitor.endNode(this, parentExp);
  }
  public boolean equals(Object object) {
    if (!(object instanceof Expression)) {
      return false;
    }

    Expression e = (Expression) object;

    if (e.getType() != getType() || e.getOperandCount() != getOperandCount()) {
      return false;
    }

    // compare operands
    int len = e.getOperandCount();
    for (int i = 0; i < len; i++) {
      if (!Util.nullSafeEquals(e.getOperand(i), getOperand(i))) {
        return false;
      }
    }

    return true;
  }
  /**
   * Convenience method to log nested expressions. Used mainly for debugging. Called from
   * "toString".
   *
   * @deprecated Since 1.1 <code>encode</code> is used to recursively print expressions.
   */
  protected void toStringBuffer(StringBuffer buf) {
    for (int i = 0; i < getOperandCount(); i++) {
      if (i > 0 || getOperandCount() == 1) {
        buf.append(" ").append(expName()).append(" ");
      }

      Object op = getOperand(i);
      if (op == null) {
        buf.append("<null>");
      } else if (op instanceof String) {
        buf.append("'").append(op).append("'");
      } else if (op instanceof Expression) {
        buf.append('(');
        ((Expression) op).toStringBuffer(buf);
        buf.append(')');
      } else {
        buf.append(String.valueOf(op));
      }
    }
  }
 /**
  * Returns a logical NOT of current expression.
  *
  * @since 1.0.6
  */
 public Expression notExp() {
   Expression exp = ExpressionFactory.expressionOfType(Expression.NOT);
   exp.setOperand(0, this);
   return exp;
 }