[jboss-cvs] jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/nestedset/query ...

Christian Bauer christian at hibernate.org
Tue Dec 18 23:29:31 EST 2007


  User: cbauer  
  Date: 07/12/18 23:29:31

  Added:       examples/wiki/src/main/org/jboss/seam/wiki/core/nestedset/query     
                        NestedSetResultTransformer.java
                        NestedSetDuplicator.java NestedSetNodeWrapper.java
                        package.html NestedSetQueryBuilder.java
  Log:
  Major rewrite of the most of the application
  
  Revision  Changes    Path
  1.1      date: 2007/12/19 04:29:31;  author: cbauer;  state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/nestedset/query/NestedSetResultTransformer.java
  
  Index: NestedSetResultTransformer.java
  ===================================================================
  /*
   * JBoss, Home of Professional Open Source
   *
   * Distributable under LGPL license.
   * See terms of license at gnu.org.
   */
  package org.jboss.seam.wiki.core.nestedset.query;
  
  import org.apache.commons.logging.Log;
  import org.apache.commons.logging.LogFactory;
  import org.hibernate.transform.ResultTransformer;
  import org.jboss.seam.wiki.core.nestedset.NestedSetNode;
  
  import java.io.Serializable;
  import java.util.*;
  
  /**
   * Transforms a nested set query result into a tree of in-memory linked {@link NestedSetNodeWrapper} instances.
   * <p>
   * This transformer expects two projected values, with the alias names <tt>nestedSetLevel</tt> and
   * <tt>NestedSetNode</tt>. Your query must return these two values in that order
   * (see {@link NestedSetQueryBuilder}). The transformer uses these values to build an in-memory
   * tree of linked {@link NestedSetNodeWrapper} instances from the flat tree in the query result.
   * </p>
   * <p>
   * You need to manually create a {@link NestedSetNodeWrapper} for the start node of your query (which
   * is not included in the query result) and pass it to the constructor of the transformer. For example:
   * </p>
   * <pre>
   * NestedSetNodeWrapper<N> startNodeWrapper = new NestedSetNodeWrapper<N>(startNode, comparator);
   * nestedSetQuery.setResultTransformer( new NestedSetResultTransformer<N>(startNodeWrapper) );
   * nestedSetQuery.list();
   * </pre>
   * <p>
   * The start node is at level zero. The actual query <tt>list()</tt> operation returns nothing,
   * the transformer takes each tuple of the result and appends it to the tree, on the
   * <tt>startNodeWrapper</tt>. You can now navigate the tree you loaded by accessing the
   * <tt>wrappedChildren</tt> collection (recursiveley all the way down) and <tt>wrappedParent</tt> property,
   * starting with the <tt>startNodeWrapper</tt> (which doesn't have a linked <tt>wrappedParent</tt>).
   * The <tt>wrappedChildrenSorted</tt> colleciton of each wrapper is sorted with the given
   * {@link java.util.Comparator}.
   * </p>
   * <p>
   * If you supply a <tt>flattenToLevel</tt> constructor argument, the transformed tree will be flattened
   * to the specified level. If, for example, you declare that the tree should be flattened to level 3, all
   * nodes that are deeper than level 3 will be appended to the parent in level 3, so that the tree is no
   * deeper than 3 levels. This is useful for certain kinds of tree display. Also note that this flattening
   * algorithm is applying the given comparator (or the default identifier comparator) during tree manipulation.
   * That means that if you set <tt>flattenToLevel</tt>, the <tt>wrappedChildren</tt> collection (a list) will
   * be correctly sorted, because sorting needs to occur when a sub-level is inserted into its parent level
   * during flattening of the tree. the <tt>wrappedChildrenSorted</tt> collection is now not in the right
   * order because it sorts afterwards, on the already flattened tree, losing sub-level sorting!
   * </p>
   * <p>
   * This transformer accepts a {@link NestedSetDuplicator} instance which will be used, if not null, to
   * copy every <tt>NestedSetNode</tt> returned by the query. This is useful if you do not want or require the
   * original <tt>NestedSetNode</tt> instances wrapped, for example, if changes on these instances (or the
   * delegate owner) should not * be reflected by the wrapped instances. You can effectively generate a stable copy of
   * the tree, e.g. for display purposes.
   * </p>
   * <p>
   * A note about restrictions: If you use the default query generated by {@link NestedSetQueryBuilder}, you
   * will have a whole and complete subtree, hence, you will not have any gaps in the in-memory tree of
   * {@link NestedSetNodeWrapper}s returned by the transformer. However, if you add another restriction condition
   * (e.g. "only return tuples <tt>where isMenuItem = true</tt>"), you will have gaps
   * in the in-memory tree. These gaps can be recursive, for example, if a subnode B has children C, D, and E, and only
   * C, D, and E have the <tt>isMenuItem</tt> flag enabled, they will not be included in the in-memory tree because
   * their parent, B, does not have the <tt>isMenuItem</tt> flag enabled. The query won't return B so its children,
   * which are returned by the query, can't be linked into the in-memory tree. They will be ignored. This might be
   * the correct behavior for building a tree of menu items, but there are certainly situations when you don't want
   * these gaps but only restrict what <i>leaf</i> nodes are included in the tree. This is currently not possible with
   * the query/transform approach.
   * </p>
   *
   * @author Christian Bauer
   */
  public class NestedSetResultTransformer<N extends NestedSetNode> implements ResultTransformer {
  
      private static final Log log = LogFactory.getLog(NestedSetResultTransformer.class);
  
      Comparator<NestedSetNodeWrapper<N>> comparator;
      NestedSetNodeWrapper<N> rootWrapper;
      NestedSetNodeWrapper<N> currentParent;
      long flattenToLevel = 0;
      Map<String, String> additionalProjections = new HashMap<String, String>();
      NestedSetDuplicator<N> nestedSetDuplicator = null;
  
      public NestedSetResultTransformer(NestedSetNodeWrapper<N> rootWrapper) {
          this(rootWrapper, 0l);
      }
  
      public NestedSetResultTransformer(NestedSetNodeWrapper<N> rootWrapper, long flattenToLevel) {
          this(rootWrapper, flattenToLevel, new HashMap<String, String>());
      }
  
      public NestedSetResultTransformer(NestedSetNodeWrapper<N> rootWrapper, long flattenToLevel, Map<String, String> additionalProjections) {
          this.rootWrapper = rootWrapper;
          this.flattenToLevel = flattenToLevel;
          this.additionalProjections = additionalProjections;
  
          this.comparator = rootWrapper.getComparator();
          currentParent = rootWrapper;
      }
  
      public NestedSetNodeWrapper<N> getRootWrapper() {
          return rootWrapper;
      }
  
      public Map<String, String> getAdditionalProjections() {
          return additionalProjections;
      }
  
      public void setNestedSetDuplicator(NestedSetDuplicator<N> nestedSetDuplicator) {
          this.nestedSetDuplicator = nestedSetDuplicator;
      }
  
      public Object transformTuple(Object[] objects, String[] aliases) {
  
          if (!NestedSetQueryBuilder.PROJECTION_LEVEL_ALIAS.equals(aliases[0]))
              throw new RuntimeException("Missing alias '"+NestedSetQueryBuilder.PROJECTION_LEVEL_ALIAS+"' as the first projected value in the nested set query");
          if (!NestedSetQueryBuilder.PROJECTION_NODE_ALIAS.equals(aliases[1]))
              throw new RuntimeException("Missing alias '"+NestedSetQueryBuilder.PROJECTION_NODE_ALIAS+"' as the second projected value in the nested set query");
          if (objects.length < 2) {
              throw new RuntimeException("Nested set query needs to at least return two values, the level and the nested set node instance");
          }
  
          Long nestedSetNodeLevel = (Long)objects[0];
          N nestedSetNode = (N)objects[1];
  
          log.trace("transforming nested set tuple into tree node: " + nestedSetNode + " at level: " + nestedSetNodeLevel);
  
          if (nestedSetNode.getParentNodeInfo() == null) {
              log.trace("node doesn't have a parent, so it's the same as the root and we are not including it in the result");
              return null;
          }
  
          Long nestedSetNodeParentId = nestedSetNode.getParentNodeInfo().getOwner().getId(); // Store the parent id before making a duplicate
          if (nestedSetDuplicator != null) nestedSetNode = nestedSetDuplicator.duplicate(nestedSetNode);
          if (nestedSetNode == null) return null; // Continue in loop if the duplicator didn't make a proper copy
  
          Map<String, Object> additionalProjectionValues = new LinkedHashMap<String, Object>();
          int i = 2;
          for (Map.Entry<String, String> entry : additionalProjections.entrySet()) {
              additionalProjectionValues.put(entry.getKey(), objects[i++]);
          }
  
          // Connect the tree hierarchically (child to parent, skip child if parent isn't present)
          NestedSetNodeWrapper<N> nodeWrapper =
                  createNestedSetNodeWrapper(nestedSetNode, comparator, rootWrapper.getLevel()+nestedSetNodeLevel, additionalProjectionValues);
          if (!nestedSetNodeParentId.equals(currentParent.getWrappedNode().getId())) {
              NestedSetNodeWrapper<N> foundParent = findParentInTree(nestedSetNodeParentId, currentParent);
              if (foundParent != null) {
                  currentParent = foundParent;
              } else {
                  return null; // Continue
              }
          }
          nodeWrapper.setWrappedParent(currentParent);
          currentParent.addWrappedChild(nodeWrapper);
          log.trace("adding nested set node to tree: " + nodeWrapper);
          currentParent = nodeWrapper;
  
          return rootWrapper; // Return just something so that transformList() will be called when we are done
      }
  
      private NestedSetNodeWrapper<N> findParentInTree(Serializable parentId, NestedSetNodeWrapper<N> startNode) {
          if (!parentId.equals(startNode.getWrappedNode().getId()) && startNode.getWrappedParent() != null) {
              return findParentInTree(parentId, startNode.getWrappedParent());
          } else if (parentId.equals(startNode.getWrappedNode().getId())) {
              return startNode;
          } else {
              return null;
          }
      }
  
      public List transformList(List list) {
          if (flattenToLevel > 0) {
              log.trace("flattening nested set tree to levels: " + flattenToLevel);
              List<NestedSetNodeWrapper<N>> flatChildren = new ArrayList<NestedSetNodeWrapper<N>>();
              for(NestedSetNodeWrapper<N> child: rootWrapper.getWrappedChildrenSorted()) {
                  flattenTree(flatChildren, child);
              }
              rootWrapper.setWrappedChildren(flatChildren);
          }
          return new ArrayList(); // Nothing is returned from this transformer
      }
  
      // Recursively flatten tree
      public void flattenTree(List<NestedSetNodeWrapper<N>> flatChildren, NestedSetNodeWrapper<N> wrapper) {
          NestedSetNodeWrapper<N> newWrapper =
                  createNestedSetNodeWrapper(wrapper.getWrappedNode(), comparator, wrapper.getLevel(), wrapper.getAdditionalProjections());
          log.trace("during flattening, adding node to tree: " + newWrapper);
          flatChildren.add( newWrapper );
  
          if (wrapper.getWrappedChildren().size() > 0 && wrapper.getLevel() < flattenToLevel) {
              log.trace("adding children of node to given level");
              for (NestedSetNodeWrapper<N> child : wrapper.getWrappedChildrenSorted()) {
                  flattenTree(newWrapper.getWrappedChildren(), child);
              }
          } else if (wrapper.getWrappedChildren().size() > 0) {
              log.trace("flattening children of node to current level, ignoring given level");
              for (NestedSetNodeWrapper<N> child : wrapper.getWrappedChildrenSorted()) {
                  flattenTree(flatChildren, child);
              }
          }
      }
  
      public NestedSetNodeWrapper<N> createNestedSetNodeWrapper(N nestedSetNode,
                                                                Comparator<NestedSetNodeWrapper<N>> comparator,
                                                                Long nestedSetNodeLevel,
                                                                Map<String, Object> additionalProjectionValues) {
          return new NestedSetNodeWrapper<N>(nestedSetNode, comparator, nestedSetNodeLevel, additionalProjectionValues);
      }
  
  }
  
  
  
  1.1      date: 2007/12/19 04:29:31;  author: cbauer;  state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/nestedset/query/NestedSetDuplicator.java
  
  Index: NestedSetDuplicator.java
  ===================================================================
  /*
   * JBoss, Home of Professional Open Source
   *
   * Distributable under LGPL license.
   * See terms of license at gnu.org.
   */
  package org.jboss.seam.wiki.core.nestedset.query;
  
  import org.jboss.seam.wiki.core.nestedset.NestedSetNode;
  
  /**
   * Generate a copy of a {@link org.jboss.seam.wiki.core.nestedset.NestedSetNode}.
   *
   * <p>
   * Used by the {@link NestedSetResultTransformer} to duplicate nodes after retrieving them
   * from the database. Useful if you need a copy of the tree for display purposes which is stable and
   * does not reflect any changes to the underlying real persistent nodes. This copy should be not
   * connected or hollow, that is, you should copy only the miminum properties you require for display
   * and probably not any collections or to-one entity references. However, it probably should copy the
   * one-to-one reference to the owner of the delegate, if that is what you are displaying.
   * </p>
   *
   * @author Christian Bauer
   */
  public interface NestedSetDuplicator<N extends NestedSetNode> {
  
      /**
       * Make a (probably hollow) copy of the given nested set delegate.
       * <p>
       * You <b>have to</b> ensure that the copy holds the same identifier value as the original.
       * </p>
       * @param nestedSetNode the persistent nested set delegate from the database
       * @return null if a copy couln't be made, skipping the node in the tree result transformation
       */
      public N duplicate(N nestedSetNode);
  }
  
  
  
  1.1      date: 2007/12/19 04:29:31;  author: cbauer;  state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/nestedset/query/NestedSetNodeWrapper.java
  
  Index: NestedSetNodeWrapper.java
  ===================================================================
  /*
   * JBoss, Home of Professional Open Source
   *
   * Distributable under LGPL license.
   * See terms of license at gnu.org.
   */
  package org.jboss.seam.wiki.core.nestedset.query;
  
  import org.jboss.seam.wiki.core.nestedset.NestedSetNode;
  
  import java.util.*;
  
  /**
   * Wraps a {@link org.jboss.seam.wiki.core.nestedset.NestedSetDelegate} and links it into a read-only tree of parent and children.
   * <p>
   * This wrapper is returned by the {@link NestedSetResultTransformer}. For example,
   * you query your tree with a nested set query starting from a particular node. You
   * want all children of that start node, including their children, and so on. The
   * {@link NestedSetResultTransformer} will handle your query result, which represents
   * a flat subtree, and link together the nodes in a hierarchical fashion. You will get
   * back your start node in a {@link NestedSetNodeWrapper} and you can access the
   * children and their children, and so on, through the <tt>wrappedChildren</tt> collection
   * of the wrapper. The regular <tt>children</tt> collection of the wrapped
   * {@link org.jboss.seam.wiki.core.nestedset.NestedSetDelegate} owner instances are not initialized! Use
   * the wrapper tree to display the data or to work with the whole subtree. As a bonus you also get
   * the <tt>level</tt> of each node in the (sub)tree you queried. You can access (but not
   * modify) the linked parent of each wrapped node through <tt>wrappedParent</tt>.
   * </p>
   * <p>
   * The <tt>wrappedChildren</tt> of each wrapper are by default in a {@link java.util.List}.
   * You can also access the same nodes through the <tt>getWrappedChildrenSorted()</tt> method,
   * which returns a {@link java.util.SortedSet} that is sorted with the {@link java.util.Comparator}
   * supplied at construction time. This means that in-level sorting (how the children of a particular node
   * are sorted) does not occur in the database but in memory. This should not be a performance problem,
   * as you'd usually query for quite small subtrees, most of the time to display a
   * subtree. The comparator usually sorts the collection by some property of the
   * wrapped {@link org.jboss.seam.wiki.core.nestedset.NestedSetDelegate} owner.
   * </p>
   * <p>
   * Note: Do not modify the collections or the parent reference of the wrapper, these
   * are read-only results and modifications are not reflected in the database.
   * </p>
   *
   * @author Christian Bauer
   */
  public class NestedSetNodeWrapper<N extends NestedSetNode> {
  
      N wrappedNode;
      NestedSetNodeWrapper<N> wrappedParent;
      List<NestedSetNodeWrapper<N>> wrappedChildren = new ArrayList<NestedSetNodeWrapper<N>>();
      Comparator<NestedSetNodeWrapper<N>> comparator;
      Long level;
      Map<String, Object> additionalProjections = new HashMap<String, Object>();
      public boolean childrenLoaded = false;
  
      public NestedSetNodeWrapper(N wrappedNode) {
          this(
              wrappedNode,
              // Default comparator uses identifiers of wrapped nodes
              new Comparator<NestedSetNodeWrapper<N>>() {
                  public int compare(NestedSetNodeWrapper<N> o1, NestedSetNodeWrapper<N> o2) {
                      return o1.getWrappedNode().getId().compareTo(o2.getWrappedNode().getId());
                  }
              }
          );
      }
  
      public NestedSetNodeWrapper(N wrappedNode, Comparator<NestedSetNodeWrapper<N>> comparator) {
          this(wrappedNode, comparator, 0l);
      }
  
      public NestedSetNodeWrapper(N wrappedNode, Comparator<NestedSetNodeWrapper<N>> comparator, Long level) {
          this(wrappedNode, comparator, level, new HashMap<String,Object>());
      }
  
      public NestedSetNodeWrapper(N wrappedNode, Comparator<NestedSetNodeWrapper<N>> comparator, Long level, Map<String,Object> additionalProjections) {
          if (wrappedNode == null) {
              throw new IllegalArgumentException("Can't wrap null node");
          }
          this.wrappedNode = wrappedNode;
          this.comparator = comparator;
          this.level = level;
          this.additionalProjections = additionalProjections;
      }
  
      public N getWrappedNode() {
          return wrappedNode;
      }
  
      void setWrappedNode(N wrappedNode) {
          this.wrappedNode = wrappedNode;
      }
  
      public NestedSetNodeWrapper<N> getWrappedParent() {
          return wrappedParent;
      }
  
      void setWrappedParent(NestedSetNodeWrapper<N> wrappedParent) {
          this.wrappedParent = wrappedParent;
          childrenLoaded = true;
      }
  
      public List<NestedSetNodeWrapper<N>> getWrappedChildren() {
          return wrappedChildren;
      }
  
      void setWrappedChildren(List<NestedSetNodeWrapper<N>> wrappedChildren) {
          this.wrappedChildren = wrappedChildren;
      }
  
      void addWrappedChild(NestedSetNodeWrapper<N> wrappedChild) {
          getWrappedChildren().add(wrappedChild);
          childrenLoaded = true;
      }
  
      public Comparator<NestedSetNodeWrapper<N>> getComparator() {
          return comparator;
      }
  
      public Long getLevel() {
          return level;
      }
  
      public Map<String, Object> getAdditionalProjections() {
          return additionalProjections;
      }
  
      public SortedSet<NestedSetNodeWrapper<N>> getWrappedChildrenSorted() {
          SortedSet<NestedSetNodeWrapper<N>> wrappedChildrenSorted = new TreeSet<NestedSetNodeWrapper<N>>(comparator);
          wrappedChildrenSorted.addAll(getWrappedChildren());
          return wrappedChildrenSorted;
      }
  
      // This is needed because JSF converters for selectitems need to return an equal() instance to
      // the selected item of the selectitems collection. This sucks.
      public boolean equals(Object o) {
          if (this == o) return true;
          if (o == null || getClass() != o.getClass()) return false;
  
          NestedSetNodeWrapper that = (NestedSetNodeWrapper) o;
  
          if (!wrappedNode.getId().equals(that.wrappedNode.getId())) return false;
  
          return true;
      }
  
      public int hashCode() {
          return wrappedNode.getId().hashCode();
      }
  
      public String toString() {
          return "Wrapper on level " + getLevel() + " for: " + getWrappedNode();
      }
  
  }
  
  
  
  
  1.1      date: 2007/12/19 04:29:31;  author: cbauer;  state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/nestedset/query/package.html
  
  Index: package.html
  ===================================================================
  <html>
  <head></head>
  <body>
  
  <p>
      Convenience classes for building nested set queries. Retrieve trees either as flat tables or as
      in-memory trees of linked parent/children collections.
  </p>
  
  </body>
  </html>
  
  
  1.1      date: 2007/12/19 04:29:31;  author: cbauer;  state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/nestedset/query/NestedSetQueryBuilder.java
  
  Index: NestedSetQueryBuilder.java
  ===================================================================
  /*
   * JBoss, Home of Professional Open Source
   *
   * Distributable under LGPL license.
   * See terms of license at gnu.org.
   */
  package org.jboss.seam.wiki.core.nestedset.query;
  
  import org.jboss.seam.wiki.core.nestedset.NestedSetNode;
  
  /**
   * Generate a simple query (or individual clauses for more complex queries) for nested set trees.
   * <p>
   * This is a convenience class, use it to build queries that return (sub)trees. The query build by
   * this class always returns a table with each tuple representing a node in the tree. The projected
   * aliases are, in order:
   * </p>
   * <ul>
   * <li><tt>nestedSetLevel</tt>, representing the level of the node in the tree
   * <li><tt>nestedSetDelegate</tt>, representing the nested set delegate, aka the node
   * </ul>
   * <p>
   * The <tt>owner</tt> of each nested set delegate is eagerly fetched, so you can access it immediately
   * after executing the query by calling <tt>nestedSetDelegate.getOwner()</tt> without the cost of an
   * additional database hit. The tree is ordered, that is, tuples that represent sub-nodes of a parent node
   * are always returned after the parent node.
   * </p>
   * <p>
   * A typical nested set query, for all subtree nodes starting at a particular node, looks as follows:
   * </p>
   * <pre>
   * NestedSetQueryBuilder nsQuery = new NestedSetQueryBuilder(startNode);
   * Query nestedSetQuery =  session.createQuery(nsQuery.getSimpleQuery());
   *
   * // Bind parameters
   * nestedSetQuery.setParameter("nsThread", startNode.getNestedSetDelegate().getNsThread());
   * nestedSetQuery.setParameter("nsLeft", startNode.getNestedSetDelegate().getNsLeft());
   * nestedSetQuery.setParameter("nsRight", startNode.getNestedSetDelegate().getNsRight());
   *
   * // Execute
   * List&lt;Object[]> tree = nestedSetQuery.list();
   *
   * // Iterate result
   * for (Object[] treeNodeTuple : tree) {
   *
   *     Long level = (Long)treeNodeTuple[0];
   *     NestedSetDelegate delegate = (NestedSetDelegate)treeNodeTuple[1];
   *
   *     // Real tree node
   *     NestedSetDelegateOwner node = delegate.getOwner();
   *     ...
   * }
   *
   * </pre>
   * <p>
   * The values for <tt>thread</tt>, <tt>startLeft</tt>, and <tt>startRight</tt> parameters are the values of
   * the root node of the subtree you want to query for. This start node is not included in the query result.
   * </p>
   * <p>
   * If you want to retrieve a tree not in tabular form but as a tree of nodes with parent/children collection
   * references, apply the {@link org.jboss.seam.wiki.core.nestedset.query.NestedSetResultTransformer} to the
   * query before executing it.
   * </p>
   * <p>
   * TODO: This class could rely on Hibernate interfaces and also do the parameter binding. It would also be
   * much nicer if we wouldn't have to group the properties of the delegate owner here, so we don't rely on
   * that interface at all.
   * </p>
   *
   *
   * @author Christian Bauer
   */
  public class NestedSetQueryBuilder {
  
      public static String NODE_ALIAS = "n1";
      public static String NODE2_ALIAS = "n2";
      public static String PROJECTION_NODE_ALIAS = "nestedSetNode";
      public static String PROJECTION_LEVEL_ALIAS = "nestedSetLevel";
  
      NestedSetNode startTree;
      boolean restrictionIncludesStart = false;
      boolean fetchLazyProperties = false;
  
      public NestedSetQueryBuilder(NestedSetNode startTree) {
          this.startTree = startTree;
      }
  
      public NestedSetQueryBuilder(NestedSetNode startTree, boolean restrictionIncludesStart) {
          this.startTree = startTree;
          this.restrictionIncludesStart = restrictionIncludesStart;
      }
  
      public NestedSetQueryBuilder(NestedSetNode startTree, boolean restrictionIncludesStart, boolean fetchLazyProperties) {
          this.startTree = startTree;
          this.restrictionIncludesStart = restrictionIncludesStart;
          this.fetchLazyProperties = fetchLazyProperties;
      }
  
      public String getSelectNodeClause() {
          StringBuilder clause = new StringBuilder();
          clause.append(NODE_ALIAS).append(" as ").append(PROJECTION_NODE_ALIAS).append(" ");
          return clause.toString();
      }
  
      public String getSelectLevelClause() {
          StringBuilder clause = new StringBuilder();
          clause.append("count(").append(NestedSetQueryBuilder.NODE_ALIAS).append(".id) as ").append(PROJECTION_LEVEL_ALIAS);
          return clause.toString();
      }
  
      public String getFromClause() {
          StringBuilder clause = new StringBuilder();
          clause.append(getEntityName()).append(" ").append(NODE_ALIAS);
          if (fetchLazyProperties) clause.append(" fetch all properties");
          clause.append(", ").append(getEntityName()).append(" ").append(NODE2_ALIAS);
          return clause.toString();
      }
  
      public String getWhereClause() {
          return getWhereClause(true);
      }
  
      public String getWhereClause(boolean restrictOnNodeInfo) {
          StringBuilder clause = new StringBuilder();
          clause.append(NODE_ALIAS).append(".nodeInfo.nsThread = ").append(NODE2_ALIAS).append(".nodeInfo.nsThread");
          clause.append(" and ").append(NODE_ALIAS);
          clause.append(".nodeInfo.nsLeft between ");
          clause.append(NODE2_ALIAS).append(".nodeInfo.nsLeft and ").append(NODE2_ALIAS).append(".nodeInfo.nsRight");
          if (restrictOnNodeInfo) {
              clause.append(" and ").append(NODE2_ALIAS).append(".nodeInfo.nsThread = :nsThread");
              clause.append(" and ").append(NODE2_ALIAS).append(".nodeInfo.nsLeft ").append(restrictionIncludesStart ? ">=" : ">").append(" :nsLeft");
              clause.append(" and ").append(NODE2_ALIAS).append(".nodeInfo.nsRight ").append(restrictionIncludesStart ? "<=" : "< ").append(" :nsRight");
          }
          return clause.toString();
      }
  
      public String getGroupByClause() {
          StringBuilder clause = new StringBuilder();
          for (int i = 0; i < startTree.getPropertiesForGroupingInQueries().length; i++) {
              clause.append(NODE_ALIAS).append(".").append(startTree.getPropertiesForGroupingInQueries()[i]);
              if (i != startTree.getPropertiesForGroupingInQueries().length-1) clause.append(", ");
          }
          if (fetchLazyProperties) {
              clause.append(", ");
              for (int i = 0; i < startTree.getLazyPropertiesForGroupingInQueries().length; i++) {
                  clause.append(NODE_ALIAS).append(".").append(startTree.getLazyPropertiesForGroupingInQueries()[i]);
                  if (i != startTree.getLazyPropertiesForGroupingInQueries().length-1) clause.append(", ");
              }
          }
          clause.append(", ").append(NODE_ALIAS).append(".nodeInfo.nsLeft");
          clause.append(", ").append(NODE_ALIAS).append(".nodeInfo.nsRight");
          clause.append(", ").append(NODE_ALIAS).append(".nodeInfo.nsThread");
          clause.append(", ").append(NODE_ALIAS).append(".id");
          return clause.toString();
      }
  
      public String getOrderByClause() {
          StringBuilder clause = new StringBuilder();
          clause.append(NODE_ALIAS).append(".nodeInfo.nsLeft asc");
          return clause.toString();
      }
  
      public String getSimpleQuery() {
          StringBuilder query = new StringBuilder();
          query.append("select").append(" ");
          query.append(getSelectLevelClause()).append(", ");
          query.append(getSelectNodeClause()).append(" from ");
          query.append(getFromClause()).append(" where ");
          query.append(getWhereClause()).append(" group by ");
          query.append(getGroupByClause()).append(" order by ");
          query.append(getOrderByClause());
          return query.toString();
      }
  
      protected String getEntityName() {
          return startTree.getClass().getName();
      }
  
  }
  
  
  



More information about the jboss-cvs-commits mailing list