Open Source Repository

Home /hibernate/hibernate-3.2.7.ga | Repository Home



org/hibernate/hql/ast/util/ASTUtil.java
/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
 * indicated by the @author tags or express copyright attribution
 * statements applied by the authors.  All third-party contributions are
 * distributed under license by Red Hat Middleware LLC.
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 *
 * This program 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 Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this distribution; if not, write to:
 * Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor
 * Boston, MA  02110-1301  USA
 */
package org.hibernate.hql.ast.util;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

import antlr.ASTFactory;
import antlr.collections.AST;
import antlr.collections.impl.ASTArray;

/**
 * Provides utility methods for AST traversal and manipulation.
 *
 @author Joshua Davis
 @author Steve Ebersole
 */
public final class ASTUtil {
  /**
   * Disallow instantiation.
   *
   @deprecated (tellclovertoignorethis)
   */
  private ASTUtil() {
  }

  /**
   * Creates a single node AST.
   <p/>
   * TODO : this is silly, remove it...
   *
   @param astFactory The factory.
   @param type The node type.
   @param text The node text.
   *
   @return AST - A single node tree.
   *
   @deprecated silly
   */
  public static AST create(ASTFactory astFactory, int type, String text) {
    return astFactory.createtype, text );
  }

  /**
   * Creates a single node AST as a sibling of the passed prevSibling,
   * taking care to reorganize the tree correctly to account for this
   * newly created node.
   *
   @param astFactory The factory.
   @param type The node type.
   @param text The node text.
   @param prevSibling The previous sibling.
   *
   @return The created AST node.
   */
  public static AST createSibling(ASTFactory astFactory, int type, String text, AST prevSibling) {
    AST node = astFactory.createtype, text );
    return insertSiblingnode, prevSibling );
  }

  /**
   * Inserts a node into a child subtree as a particularly positioned
   * sibling taking care to properly reorganize the tree to account for this
   * new addition.
   *
   @param node The node to insert
   @param prevSibling The previous node at the sibling position
   * where we want this node inserted.
   *
   @return The return is the same as the node parameter passed in.
   */
  public static AST insertSibling(AST node, AST prevSibling) {
    node.setNextSiblingprevSibling.getNextSibling() );
    prevSibling.setNextSiblingnode );
    return node;
  }

  /**
   * Creates a 'binary operator' subtree, given the information about the
   * parent and the two child nodex.
   *
   @param factory The AST factory.
   @param parentType The type of the parent node.
   @param parentText The text of the parent node.
   @param child1 The first child.
   @param child2 The second child.
   *
   @return AST - A new sub-tree of the form "(parent child1 child2)"
   */
  public static AST createBinarySubtree(ASTFactory factory, int parentType, String parentText, AST child1, AST child2) {
    ASTArray array = createAstArrayfactory, 3, parentType, parentText, child1 );
    array.addchild2 );
    return factory.makearray );
  }

  /**
   * Creates a single parent of the specified child (i.e. a 'unary operator'
   * subtree).
   *
   @param factory The AST factory.
   @param parentType The type of the parent node.
   @param parentText The text of the parent node.
   @param child The child.
   *
   @return AST - A new sub-tree of the form "(parent child)"
   */
  public static AST createParent(ASTFactory factory, int parentType, String parentText, AST child) {
    ASTArray array = createAstArrayfactory, 2, parentType, parentText, child );
    return factory.makearray );
  }

  public static AST createTree(ASTFactory factory, AST[] nestedChildren) {
    AST[] array = new AST[2];
    int limit = nestedChildren.length - 1;
    for int i = limit; i >= 0; i-- ) {
      if i != limit ) {
        array[1= nestedChildren[i + 1];
        array[0= nestedChildren[i];
        factory.makearray );
      }
    }
    return array[0];
  }

  /**
   * Determine if a given node (test) is contained anywhere in the subtree
   * of another given node (fixture).
   *
   @param fixture The node against which to testto be checked for children.
   @param test The node to be tested as being a subtree child of the parent.
   @return True if child is contained in the parent's collection of children.
   */
  public static boolean isSubtreeChild(AST fixture, AST test) {
    AST n = fixture.getFirstChild();
    while n != null ) {
      if n == test ) {
        return true;
      }
      if n.getFirstChild() != null && isSubtreeChildn, test ) ) {
        return true;
      }
      n = n.getNextSibling();
    }
    return false;
  }

  /**
   * Finds the first node of the specified type in the chain of children.
   *
   @param parent The parent
   @param type The type to find.
   *
   @return The first node of the specified type, or null if not found.
   */
  public static AST findTypeInChildren(AST parent, int type) {
    AST n = parent.getFirstChild();
    while n != null && n.getType() != type ) {
      n = n.getNextSibling();
    }
    return n;
  }

  /**
   * Returns the last direct child of 'n'.
   *
   @param n The parent
   *
   @return The last direct child of 'n'.
   */
  public static AST getLastChild(AST n) {
    return getLastSiblingn.getFirstChild() );
  }

  /**
   * Returns the last sibling of 'a'.
   *
   @param a The sibling.
   *
   @return The last sibling of 'a'.
   */
  private static AST getLastSibling(AST a) {
    AST last = null;
    while a != null ) {
      last = a;
      a = a.getNextSibling();
    }
    return last;
  }

  /**
   * Returns the 'list' representation with some brackets around it for debugging.
   *
   @param n The tree.
   *
   @return The list representation of the tree.
   */
  public static String getDebugString(AST n) {
    StringBuffer buf = new StringBuffer();
    buf.append"[ " );
    buf.append( ( n == null "{null}" : n.toStringTree() );
    buf.append" ]" );
    return buf.toString();
  }

  /**
   * Find the previous sibling in the parent for the given child.
   *
   @param parent the parent node
   @param child the child to find the previous sibling of
   *
   @return the previous sibling of the child
   */
  public static AST findPreviousSibling(AST parent, AST child) {
    AST prev = null;
    AST n = parent.getFirstChild();
    while n != null ) {
      if n == child ) {
        return prev;
      }
      prev = n;
      n = n.getNextSibling();
    }
    throw new IllegalArgumentException"Child not found in parent!" );
  }

  /**
   * Makes the child node a sibling of the parent, reconnecting all siblings.
   *
   @param parent the parent
   @param child the child
   */
  public static void makeSiblingOfParent(AST parent, AST child) {
    AST prev = findPreviousSiblingparent, child );
    if prev != null ) {
      prev.setNextSiblingchild.getNextSibling() );
    }
    else // child == parent.getFirstChild()
      parent.setFirstChildchild.getNextSibling() );
    }
    child.setNextSiblingparent.getNextSibling() );
    parent.setNextSiblingchild );
  }

  public static String getPathText(AST n) {
    StringBuffer buf = new StringBuffer();
    getPathTextbuf, n );
    return buf.toString();
  }

  private static void getPathText(StringBuffer buf, AST n) {
    AST firstChild = n.getFirstChild();
    // If the node has a first child, recurse into the first child.
    if firstChild != null ) {
      getPathTextbuf, firstChild );
    }
    // Append the text of the current node.
    buf.appendn.getText() );
    // If there is a second child (RHS), recurse into that child.
    if firstChild != null && firstChild.getNextSibling() != null ) {
      getPathTextbuf, firstChild.getNextSibling() );
    }
  }

  public static boolean hasExactlyOneChild(AST n) {
    return n != null && n.getFirstChild() != null && n.getFirstChild().getNextSibling() == null;
  }

  public static void appendSibling(AST n, AST s) {
    while n.getNextSibling() != null ) {
      n = n.getNextSibling();
    }
    n.setNextSibling);
  }

  /**
   * Inserts the child as the first child of the parent, all other children are shifted over to the 'right'.
   *
   @param parent the parent
   @param child the new first child
   */
  public static void insertChild(AST parent, AST child) {
    if parent.getFirstChild() == null ) {
      parent.setFirstChildchild );
    }
    else {
      AST n = parent.getFirstChild();
      parent.setFirstChildchild );
      child.setNextSibling);
    }
  }

  private static ASTArray createAstArray(ASTFactory factory, int size, int parentType, String parentText, AST child1) {
    ASTArray array = new ASTArraysize );
    array.addfactory.createparentType, parentText ) );
    array.addchild1 );
    return array;
  }

  /**
   * Filters nodes out of a tree.
   */
  public static interface FilterPredicate {
    /**
     * Returns true if the node should be filtered out.
     *
     @param n The node.
     *
     @return true if the node should be filtered out, false to keep the node.
     */
    boolean exclude(AST n);
  }

  /**
   * A predicate that uses inclusion, rather than exclusion semantics.
   */
  public abstract static class IncludePredicate implements FilterPredicate {
    public final boolean exclude(AST node) {
      return !includenode );
    }

    public abstract boolean include(AST node);
  }

  public static List collectChildren(AST root, FilterPredicate predicate) {
    return new CollectingNodeVisitorpredicate ).collectroot );
  }

  private static class CollectingNodeVisitor implements NodeTraverser.VisitationStrategy {
    private final FilterPredicate predicate;
    private final List collectedNodes = new ArrayList();

    public CollectingNodeVisitor(FilterPredicate predicate) {
      this.predicate = predicate;
    }

    public void visit(AST node) {
      if predicate == null || !predicate.excludenode ) ) {
        collectedNodes.addnode );
      }
    }

    public List getCollectedNodes() {
      return collectedNodes;
    }

    public List collect(AST root) {
      NodeTraverser traverser = new NodeTraverserthis );
      traverser.traverseDepthFirstroot );
      return collectedNodes;
    }
  }

  /**
   * Method to generate a map of token type names, keyed by their token type values.
   *
   @param tokenTypeInterface The *TokenTypes interface (or implementor of said interface).
   @return The generated map.
   */
  public static Map generateTokenNameCache(Class tokenTypeInterface) {
    final Field[] fields = tokenTypeInterface.getFields();
    Map cache = new HashMap( (int)( fields.length * .75 );
    for int i = 0; i < fields.length; i++ ) {
      final Field field = fields[i];
      if Modifier.isStaticfield.getModifiers() ) ) {
        try {
          cache.putfield.getnull ), field.getName() );
        }
        catch Throwable ignore ) {
        }
      }
    }
    return cache;
  }

  /**
   * Get the name of a constant defined on the given class which has the given value.
   <p/>
   * Note, if multiple constants have this value, the first will be returned which is known to be different
   * on different JVM implementations.
   *
   @param owner The class which defines the constant
   @param value The value of the constant.
   *
   @return The token type name, *or* the integer value if the name could not be found.
   *
   @deprecated Use #getTokenTypeName instead
   */
  public static String getConstantName(Class owner, int value) {
    return getTokenTypeNameowner, value );
  }

  /**
   * Intended to retrieve the name of an AST token type based on the token type interface.  However, this
   * method can be used to look up the name of any constant defined on a class/interface based on the constant value.
   * Note that if multiple constants have this value, the first will be returned which is known to be different
   * on different JVM implementations.
   *
   @param tokenTypeInterface The *TokenTypes interface (or one of its implementors).
   @param tokenType The token type value.
   *
   @return The corresponding name.
   */
  public static String getTokenTypeName(Class tokenTypeInterface, int tokenType) {
    String tokenTypeName = Integer.toStringtokenType );
    if tokenTypeInterface != null ) {
      Field[] fields = tokenTypeInterface.getFields();
      for int i = 0; i < fields.length; i++ ) {
        final Integer fieldValue = extractIntegerValuefields[i] );
        if fieldValue != null && fieldValue.intValue() == tokenType ) {
          tokenTypeName = fields[i].getName();
          break;
        }
      }
    }
    return tokenTypeName;
  }

  private static Integer extractIntegerValue(Field field) {
    Integer rtn = null;
    try {
      Object value = field.getnull );
      if value instanceof Integer ) {
        rtn = Integer value;
      }
      else if value instanceof Short ) {
        rtn = new Integer( ( ( Short value ).intValue() );
      }
      else if value instanceof Long ) {
        if ( ( ( Long value ).longValue() <= Integer.MAX_VALUE ) {
          rtn = new Integer( ( ( Long value ).intValue() );
        }
      }
    }
    catch IllegalAccessException ignore ) {
    }
    return rtn;
  }
}