[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<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