/*
* Copyright (C) 2010 eXo Platform SAS.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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 software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.xcmis.search.query;
import org.apache.commons.lang.Validate;
import org.xcmis.search.VisitException;
import org.xcmis.search.Visitors;
import org.xcmis.search.model.Limit;
import org.xcmis.search.model.Query;
import org.xcmis.search.model.column.Column;
import org.xcmis.search.model.constraint.And;
import org.xcmis.search.model.constraint.ChildNode;
import org.xcmis.search.model.constraint.Comparison;
import org.xcmis.search.model.constraint.Constraint;
import org.xcmis.search.model.constraint.DescendantNode;
import org.xcmis.search.model.constraint.FullTextSearch;
import org.xcmis.search.model.constraint.Not;
import org.xcmis.search.model.constraint.Operator;
import org.xcmis.search.model.constraint.Or;
import org.xcmis.search.model.constraint.PropertyExistence;
import org.xcmis.search.model.constraint.SameNode;
import org.xcmis.search.model.operand.BindVariableName;
import org.xcmis.search.model.operand.DynamicOperand;
import org.xcmis.search.model.operand.FullTextSearchScore;
import org.xcmis.search.model.operand.Length;
import org.xcmis.search.model.operand.Literal;
import org.xcmis.search.model.operand.LowerCase;
import org.xcmis.search.model.operand.NodeDepth;
import org.xcmis.search.model.operand.NodeLocalName;
import org.xcmis.search.model.operand.NodeName;
import org.xcmis.search.model.operand.PropertyValue;
import org.xcmis.search.model.operand.UpperCase;
import org.xcmis.search.model.ordering.Order;
import org.xcmis.search.model.ordering.Ordering;
import org.xcmis.search.model.source.Join;
import org.xcmis.search.model.source.Selector;
import org.xcmis.search.model.source.SelectorName;
import org.xcmis.search.model.source.Source;
import org.xcmis.search.model.source.join.ChildNodeJoinCondition;
import org.xcmis.search.model.source.join.DescendantNodeJoinCondition;
import org.xcmis.search.model.source.join.EquiJoinCondition;
import org.xcmis.search.model.source.join.JoinCondition;
import org.xcmis.search.model.source.join.JoinType;
import org.xcmis.search.model.source.join.SameNodeJoinCondition;
import org.xcmis.search.value.CastSystem;
import org.xcmis.search.value.PropertyType;
import java.math.BigDecimal;
import java.net.URI;
import java.util.Calendar;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* A component that can be used to programmatically create {@link QueryCommand}
* objects. Simply call methods to build the selector clause, from clause, join
* criteria, where criteria, limits, and ordering, and then {@link #query()
* obtain the query}. This builder should be adequate for most queries; however,
* any query that cannot be expressed by this builder can always be constructed
* by directly creating the Abstract Query Model classes.
* <p>
* This builder is stateful and therefore should only be used by one thread at a
* time. However, once a query has been built, the builder can be
* {@link #clear() cleared} and used to create another query.
* </p>
* <p>
* The order in which the methods are called are (for the most part) important.
* Simply call the methods in the same order that would be most natural in a
* normal SQL query. For example, the following code creates a Query object that
* is equivalent to " <code>SELECT * FROM table</code>":
*
* <pre>
* QueryCommand query = builder.selectStar().from("table").query();
* </pre>
*
* </p>
* <p>
* Here are a few other examples:
* <table border="1" cellspacing="0" cellpadding="3" summary="">
* <tr>
* <th>SQL Statement</th>
* <th>QueryBuilder code</th>
* </tr>
* <tr>
* <td>
*
* <pre>
* SELECT * FROM table1
* INNER JOIN table2
* ON table2.c0 = table1.c0
* </pre>
*
* </td>
* <td>
*
* <pre>
* query = builder.selectStar().from("table1").join("table2").on("table2.c0=table1.c0").query();
* </pre>
*
* </td>
* </tr>
* <tr>
* <td>
*
* <pre>
* SELECT * FROM table1 AS t1
* INNER JOIN table2 AS t2
* ON t1.c0 = t2.c0
* </pre>
*
* </td>
* <td>
*
* <pre>
* query = builder.selectStar().from("table1 AS t1").join("table2 AS t2").on("t1.c0=t2.c0").query();
* </pre>
*
* </td>
* </tr>
* <tr>
* <td>
*
* <pre>
* SELECT * FROM table1 AS t1
* INNER JOIN table2 AS t2
* ON t1.c0 = t2.c0
* INNER JOIN table3 AS t3
* ON t1.c1 = t3.c1
* </pre>
*
* </td>
* <td>
*
* <pre>
* query = builder.selectStar()
* .from("table1 AS t1")
* .innerJoin("table2 AS t2")
* .on("t1.c0=t2.c0")
* .innerJoin("table3 AS t3")
* .on("t1.c1=t3.c1")
* .query();
* </pre>
*
* </td>
* </tr>
* </table>
* </pre>
*/
public class QueryBuilder
{
protected final CastSystem castSystem;
protected Source source = new Selector(new SelectorName("__not:defined__"));;
protected Constraint constraint;
protected List<Column> columns = new LinkedList<Column>();
protected List<Ordering> orderings = new LinkedList<Ordering>();
protected Limit limit = Limit.NONE;
protected boolean distinct;
protected Query firstQuery;
protected boolean firstQueryAll;
/**
* Create a new builder that uses the supplied execution context.
*
* @param context
* the execution context
* @throws IllegalArgumentException
* if the context is null
*/
public QueryBuilder(CastSystem castSystem)
{
Validate.notNull(castSystem, "The context argument may not be null");
this.castSystem = castSystem;
}
/**
* Clear this builder completely to start building a new query.
*
* @return this builder object, for convenience in method chaining
*/
public QueryBuilder clear()
{
return clear(true);
}
/**
* Utility method that does all the work of the clear, but with a flag that
* defines whether to clear the first query. This method is used by
* {@link #clear()} as well as the {@link #union() many} {@link #intersect()
* set} {@link #except() operations}.
*
* @param clearFirstQuery
* true if the first query should be cleared, or false if the first
* query should be retained
* @return this builder object, for convenience in method chaining
*/
protected QueryBuilder clear(boolean clearFirstQuery)
{
source = new Selector(new SelectorName("__not:defined__"));
constraint = null;
columns = new LinkedList<Column>();
orderings = new LinkedList<Ordering>();
limit = Limit.NONE;
distinct = false;
if (clearFirstQuery)
{
this.firstQuery = null;
}
return this;
}
/**
* Convenience method that creates a selector name object using the supplied
* string.
*
* @param name
* the name of the selector; may not be null
* @return the selector name; never null
*/
protected SelectorName selector(String name)
{
return new SelectorName(name.trim());
}
/**
* Convenience method that creates a {@link Selector} object given a string
* that contains the selector name and optionally an alias. The format of the
* string parameter is <code>name [AS alias]</code>. Leading and trailing
* whitespace are trimmed.
*
* @param nameWithOptionalAlias
* the name and optional alias; may not be null
* @return the named selector object; never null
*/
protected Selector namedSelector(String nameWithOptionalAlias)
{
String[] parts = nameWithOptionalAlias.split("\\sAS\\s");
if (parts.length == 2)
{
return new Selector(selector(parts[0]), selector(parts[1]));
}
return new Selector(selector(parts[0]));
}
/**
* Create a {@link Column} given the supplied expression. The expression has
* the form "<code>[tableName.]columnName</code>", where "
* <code>tableName</code>" must be a valid table name or alias. If the table
* name/alias is not specified, then there is expected to be a single FROM
* clause with a single named selector.
*
* @param nameExpression
* the expression specifying the columm name and (optionally) the
* table's name or alias; may not be null
* @return the column; never null
* @throws IllegalArgumentException
* if the table's name/alias is not specified, but the query has
* more than one named source
*/
protected Column column(String nameExpression)
{
String[] parts = nameExpression.split("(?<!\\\\)\\."); // a . not preceded
// by an escaping
// slash
for (int i = 0; i != parts.length; ++i)
{
parts[i] = parts[i].trim();
}
SelectorName name = null;
String propertyName = null;
String columnName = null;
if (parts.length == 2)
{
name = selector(parts[0]);
propertyName = parts[1];
columnName = parts[1];
}
else
{
if (source == null)
{
name = selector(parts[0]);
propertyName = parts[0];
columnName = parts[0];
}
else if (source instanceof Selector)
{
Selector selector = (Selector)source;
name = selector.hasAlias() ? selector.getAlias() : selector.getName();
propertyName = parts[0];
columnName = parts[0];
}
else
{
throw new IllegalArgumentException("Column parts " + parts[0] + " must be scoped");
}
}
return new Column(name, propertyName, columnName);
}
/**
* Select all of the single-valued columns.
*
* @return this builder object, for convenience in method chaining
*/
public QueryBuilder selectStar()
{
columns.clear();
return this;
}
/**
* Add to the select clause the columns with the supplied names. Each column
* name has the form " <code>[tableName.]columnName</code>", where "
* <code>tableName</code>" must be a valid table name or alias. If the table
* name/alias is not specified, then there is expected to be a single FROM
* clause with a single named selector.
*
* @param columnNames
* the column expressions; may not be null
* @return this builder object, for convenience in method chaining
* @throws IllegalArgumentException
* if the table's name/alias is not specified, but the query has
* more than one named source
*/
public QueryBuilder select(String... columnNames)
{
for (String expression : columnNames)
{
columns.add(column(expression));
}
return this;
}
/**
* Select all of the distinct values from the single-valued columns.
*
* @return this builder object, for convenience in method chaining
*/
public QueryBuilder selectDistinctStar()
{
distinct = true;
return selectStar();
}
/**
* Select the distinct values from the columns with the supplied names. Each
* column name has the form " <code>[tableName.]columnName</code>", where "
* <code>tableName</code>" must be a valid table name or alias. If the table
* name/alias is not specified, then there is expected to be a single FROM
* clause with a single named selector.
*
* @param columnNames
* the column expressions; may not be null
* @return this builder object, for convenience in method chaining
* @throws IllegalArgumentException
* if the table's name/alias is not specified, but the query has
* more than one named source
*/
public QueryBuilder selectDistinct(String... columnNames)
{
distinct = true;
return select(columnNames);
}
/**
* Specify the name of the table from which tuples should be selected. The
* supplied string is of the form " <code>tableName [AS alias]</code>".
*
* @param tableNameWithOptionalAlias
* the name of the table, optionally including the alias
* @return this builder object, for convenience in method chaining
*/
public QueryBuilder from(String tableNameWithOptionalAlias)
{
Selector selector = namedSelector(tableNameWithOptionalAlias);
SelectorName oldName = this.source instanceof Selector ? ((Selector)source).getName() : null;
// Go through the columns and change the selector name to use the new
// alias ...
for (int i = 0; i != columns.size(); ++i)
{
Column old = columns.get(i);
if (old.getSelectorName().equals(oldName))
{
columns.set(i, new Column(selector.getAliasOrName(), old.getPropertyName(), old.getColumnName()));
}
}
this.source = selector;
return this;
}
/**
* Begin the WHERE clause for this query by obtaining the constraint builder.
* When completed, be sure to call {@link ConstraintBuilder#end() end()} on
* the resulting constraint builder, or else the constraint will not be
* applied to the current query.
*
* @return the constraint builder that can be used to specify the criteria;
* never null
*/
public ConstraintBuilder where()
{
return new ConstraintBuilder(null);
}
/**
* Perform an inner join between the already defined source with the supplied
* table. The supplied string is of the form "
* <code>tableName [AS alias]</code>".
*
* @param tableName
* the name of the table, optionally including the alias
* @return the component that must be used to complete the join
* specification; never null
*/
public JoinClause join(String tableName)
{
return innerJoin(tableName);
}
/**
* Perform an inner join between the already defined source with the supplied
* table. The supplied string is of the form "
* <code>tableName [AS alias]</code>".
*
* @param tableName
* the name of the table, optionally including the alias
* @return the component that must be used to complete the join
* specification; never null
*/
public JoinClause innerJoin(String tableName)
{
// Expect there to be a source already ...
return new JoinClause(namedSelector(tableName), JoinType.INNER);
}
/**
* Perform a left outer join between the already defined source with the
* supplied table. The supplied string is of the form "
* <code>tableName [AS alias]</code>".
*
* @param tableName
* the name of the table, optionally including the alias
* @return the component that must be used to complete the join
* specification; never null
*/
public JoinClause leftOuterJoin(String tableName)
{
// Expect there to be a source already ...
return new JoinClause(namedSelector(tableName), JoinType.LEFT_OUTER);
}
/**
* Perform a right outer join between the already defined source with the
* supplied table. The supplied string is of the form "
* <code>tableName [AS alias]</code>".
*
* @param tableName
* the name of the table, optionally including the alias
* @return the component that must be used to complete the join
* specification; never null
*/
public JoinClause rightOuterJoin(String tableName)
{
// Expect there to be a source already ...
return new JoinClause(namedSelector(tableName), JoinType.RIGHT_OUTER);
}
/**
* Specify the maximum number of rows that are to be returned in the results.
* By default there is no limit.
*
* @param rowLimit
* the maximum number of rows
* @return this builder object, for convenience in method chaining
* @throws IllegalArgumentException
* if the row limit is not a positive integer
*/
public QueryBuilder limit(int rowLimit)
{
this.limit = this.limit.withRowLimit(rowLimit);
return this;
}
/**
* Specify the number of rows that results are to skip. The default offset is
* '0'.
*
* @param offset
* the number of rows before the results are to begin
* @return this builder object, for convenience in method chaining
* @throws IllegalArgumentException
* if the row limit is a negative integer
*/
public QueryBuilder offset(int offset)
{
this.limit = this.limit.withOffset(offset);
return this;
}
/**
* Obtain a builder that will create the order-by clause (with one or more
* {@link Ordering} statements) for the query. This method need be called
* only once to build the order-by clause, but can be called multiple times
* (it merely adds additional {@link Ordering} statements).
*
* @return the order-by builder; never null
*/
public OrderByBuilder orderBy()
{
return new OrderByBuilder();
}
/**
* Return a {@link QueryCommand} representing the currently-built query.
*
* @return the resulting query command; never null
* @see #clear()
*/
public Query query()
{
Query result = new Query(source, constraint, orderings, columns, limit);
if (this.firstQuery != null)
{
}
return result;
}
public interface OrderByOperandBuilder
{
/**
* Adds to the order-by clause by using the length of the value for the
* given table and property.
*
* @param table
* the name of the table; may not be null and must refer to a
* valid name or alias of a table appearing in the FROM clause
* @param property
* the name of the property; may not be null and must refer to a
* valid property name
* @return the interface for completing the order-by specification; never
* null
*/
public OrderByBuilder length(String table, String property);
/**
* Adds to the order-by clause by using the value for the given table and
* property.
*
* @param table
* the name of the table; may not be null and must refer to a
* valid name or alias of a table appearing in the FROM clause
* @param property
* the name of the property; may not be null and must refer to a
* valid property name
* @return the interface for completing the order-by specification; never
* null
*/
public OrderByBuilder propertyValue(String table, String property);
/**
* Adds to the order-by clause by using the full-text search score for the
* given table.
*
* @param table
* the name of the table; may not be null and must refer to a
* valid name or alias of a table appearing in the FROM clause
* @return the interface for completing the order-by specification; never
* null
*/
public OrderByBuilder fullTextSearchScore(String table);
/**
* Adds to the order-by clause by using the depth of the node given by the
* named table.
*
* @param table
* the name of the table; may not be null and must refer to a
* valid name or alias of a table appearing in the FROM clause
* @return the interface for completing the order-by specification; never
* null
*/
public OrderByBuilder depth(String table);
/**
* Adds to the order-by clause by using the local name of the node given
* by the named table.
*
* @param table
* the name of the table; may not be null and must refer to a
* valid name or alias of a table appearing in the FROM clause
* @return the interface for completing the order-by specification; never
* null
*/
public OrderByBuilder nodeLocalName(String table);
/**
* Adds to the order-by clause by using the node name (including
* namespace) of the node given by the named table.
*
* @param table
* the name of the table; may not be null and must refer to a
* valid name or alias of a table appearing in the FROM clause
* @return the interface for completing the order-by specification; never
* null
*/
public OrderByBuilder nodeName(String table);
/**
* Adds to the order-by clause by using the uppercase form of the next
* operand.
*
* @return the interface for completing the order-by specification; never
* null
*/
public OrderByOperandBuilder upperCaseOf();
/**
* Adds to the order-by clause by using the lowercase form of the next
* operand.
*
* @return the interface for completing the order-by specification; never
* null
*/
public OrderByOperandBuilder lowerCaseOf();
}
/**
* The component used to build the order-by clause. When the clause is
* completed, {@link #end()} should be called to return to the
* {@link QueryBuilder} instance.
*/
public class OrderByBuilder
{
protected OrderByBuilder()
{
}
/**
* Begin specifying an order-by specification using
* {@link Order#ASCENDING ascending order}.
*
* @return the interface for specifying the operand that is to be ordered;
* never null
*/
public OrderByOperandBuilder ascending()
{
return new SingleOrderByOperandBuilder(this, Order.ASCENDING);
}
/**
* Begin specifying an order-by specification using
* {@link Order#DESCENDING descending order}.
*
* @return the interface for specifying the operand that is to be ordered;
* never null
*/
public OrderByOperandBuilder descending()
{
return new SingleOrderByOperandBuilder(this, Order.DESCENDING);
}
/**
* An optional convenience method that returns this builder, but which
* makes the code using this builder more readable.
*
* @return this builder; never null
*/
public OrderByBuilder then()
{
return this;
}
/**
* Complete the order-by clause and return the QueryBuilder instance.
*
* @return the query builder instance; never null
*/
public QueryBuilder end()
{
return QueryBuilder.this;
}
}
protected class SingleOrderByOperandBuilder implements OrderByOperandBuilder
{
private final Order order;
private final OrderByBuilder builder;
protected SingleOrderByOperandBuilder(OrderByBuilder builder, Order order)
{
this.order = order;
this.builder = builder;
}
protected OrderByBuilder addOrdering(DynamicOperand operand)
{
Ordering ordering = new Ordering(operand, order);
QueryBuilder.this.orderings.add(ordering);
return builder;
}
/**
* {@inheritDoc}
*
* @see org.modeshape.graph.query.QueryBuilder.OrderByOperandBuilder#propertyValue(java.lang.String,
* java.lang.String)
*/
public OrderByBuilder propertyValue(String table, String property)
{
return addOrdering(new PropertyValue(selector(table), property));
}
/**
* {@inheritDoc}
*
* @see org.modeshape.graph.query.QueryBuilder.OrderByOperandBuilder#length(java.lang.String,
* java.lang.String)
*/
public OrderByBuilder length(String table, String property)
{
return addOrdering(new Length(new PropertyValue(selector(table), property)));
}
/**
* {@inheritDoc}
*
* @see org.modeshape.graph.query.QueryBuilder.OrderByOperandBuilder#fullTextSearchScore(java.lang.String)
*/
public OrderByBuilder fullTextSearchScore(String table)
{
return addOrdering(new FullTextSearchScore(selector(table)));
}
/**
* {@inheritDoc}
*
* @see org.modeshape.graph.query.QueryBuilder.OrderByOperandBuilder#depth(java.lang.String)
*/
public OrderByBuilder depth(String table)
{
return addOrdering(new NodeDepth(selector(table)));
}
/**
* {@inheritDoc}
*
* @see org.modeshape.graph.query.QueryBuilder.OrderByOperandBuilder#nodeName(java.lang.String)
*/
public OrderByBuilder nodeName(String table)
{
return addOrdering(new NodeName(selector(table)));
}
/**
* {@inheritDoc}
*
* @see org.modeshape.graph.query.QueryBuilder.OrderByOperandBuilder#nodeLocalName(java.lang.String)
*/
public OrderByBuilder nodeLocalName(String table)
{
return addOrdering(new NodeLocalName(selector(table)));
}
/**
* {@inheritDoc}
*
* @see org.modeshape.graph.query.QueryBuilder.OrderByOperandBuilder#lowerCaseOf()
*/
public OrderByOperandBuilder lowerCaseOf()
{
return new SingleOrderByOperandBuilder(builder, order)
{
/**
* {@inheritDoc}
*
* @see org.modeshape.graph.query.QueryBuilder.SingleOrderByOperandBuilder#addOrdering(org.modeshape.graph.query.model.DynamicOperand)
*/
@Override
protected OrderByBuilder addOrdering(DynamicOperand operand)
{
return super.addOrdering(new LowerCase(operand));
}
};
}
/**
* {@inheritDoc}
*
* @see org.modeshape.graph.query.QueryBuilder.OrderByOperandBuilder#upperCaseOf()
*/
public OrderByOperandBuilder upperCaseOf()
{
return new SingleOrderByOperandBuilder(builder, order)
{
/**
* {@inheritDoc}
*
* @see org.modeshape.graph.query.QueryBuilder.SingleOrderByOperandBuilder#addOrdering(org.modeshape.graph.query.model.DynamicOperand)
*/
@Override
protected OrderByBuilder addOrdering(DynamicOperand operand)
{
return super.addOrdering(new UpperCase(operand));
}
};
}
}
/**
* Class used to specify a join clause of a query.
*
* @see QueryBuilder#join(String)
* @see QueryBuilder#innerJoin(String)
* @see QueryBuilder#leftOuterJoin(String)
* @see QueryBuilder#rightOuterJoin(String)
* @see QueryBuilder#fullOuterJoin(String)
*/
public class JoinClause
{
private final Selector rightSource;
private final JoinType type;
protected JoinClause(Selector rightTable, JoinType type)
{
this.rightSource = rightTable;
this.type = type;
}
/**
* Walk the current source or the 'rightSource' to find the named selector
* with the supplied name or alias
*
* @param tableName
* the table name
* @return the selector name matching the supplied table name; never null
* @throws IllegalArgumentException
* if the table name could not be resolved
*/
protected SelectorName nameOf(String tableName)
{
final SelectorName name = new SelectorName(tableName);
// Look at the right source ...
if (rightSource.getAliasOrName().equals(name))
{
return name;
}
// Look through the left source ...
final AtomicBoolean notFound = new AtomicBoolean(true);
try
{
Visitors.visit(source, new Visitors.AbstractModelVisitor()
{
@Override
public void visit(Selector selector)
{
if (notFound.get() && selector.getAliasOrName().equals(name))
{
notFound.set(false);
}
}
});
}
catch (VisitException e)
{
// ignore
}
if (notFound.get())
{
throw new IllegalArgumentException("Expected \"" + tableName + "\" to be a valid table name or alias");
}
return name;
}
/**
* Define the join as using an equi-join criteria by specifying the
* expression equating two columns. Each column reference must be
* qualified with the appropriate table name or alias.
*
* @param columnEqualExpression
* the equality expression between the two tables; may not be
* null
* @return the query builder instance, for method chaining purposes
* @throws IllegalArgumentException
* if the supplied expression is not an equality expression
*/
public QueryBuilder on(String columnEqualExpression)
{
String[] parts = columnEqualExpression.split("=");
if (parts.length != 2)
{
throw new IllegalArgumentException("Expected equality expression for columns, but found \""
+ columnEqualExpression + "\"");
}
return createJoin(new EquiJoinCondition(column(parts[0]), column(parts[1])));
}
/**
* Define the join criteria to require the two tables represent the same
* node. The supplied tables must be a valid name or alias.
*
* @param table1
* the name or alias of the first table
* @param table2
* the name or alias of the second table
* @return the query builder instance, for method chaining purposes
*/
public QueryBuilder onSameNode(String table1, String table2)
{
return createJoin(new SameNodeJoinCondition(nameOf(table1), nameOf(table2)));
}
/**
* Define the join criteria to require the node in one table is a
* descendant of the node in another table. The supplied tables must be a
* valid name or alias.
*
* @param ancestorTable
* the name or alias of the table containing the ancestor node
* @param descendantTable
* the name or alias of the table containing the descendant node
* @return the query builder instance, for method chaining purposes
*/
public QueryBuilder onDescendant(String ancestorTable, String descendantTable)
{
return createJoin(new DescendantNodeJoinCondition(nameOf(ancestorTable), nameOf(descendantTable)));
}
/**
* Define the join criteria to require the node in one table is a child of
* the node in another table. The supplied tables must be a valid name or
* alias.
*
* @param parentTable
* the name or alias of the table containing the parent node
* @param childTable
* the name or alias of the table containing the child node
* @return the query builder instance, for method chaining purposes
*/
public QueryBuilder onChildNode(String parentTable, String childTable)
{
return createJoin(new ChildNodeJoinCondition(nameOf(parentTable), nameOf(childTable)));
}
protected QueryBuilder createJoin(JoinCondition condition)
{
// Otherwise, just create using usual precedence ...
source = new Join(source, type, rightSource, condition);
return QueryBuilder.this;
}
}
/**
* Interface that defines a dynamic operand portion of a criteria.
*/
public interface DynamicOperandBuilder
{
/**
* Constrains the nodes in the the supplied table such that they must have
* a property value whose length matches the criteria.
*
* @param table
* the name of the table; may not be null and must refer to a
* valid name or alias of a table appearing in the FROM clause
* @param property
* the name of the property; may not be null and must refer to a
* valid property name
* @return the interface for completing the value portion of the criteria
* specification; never null
*/
public ComparisonBuilder length(String table, String property);
/**
* Constrains the nodes in the the supplied table such that they must have
* a matching value for the named property.
*
* @param table
* the name of the table; may not be null and must refer to a
* valid name or alias of a table appearing in the FROM clause
* @param property
* the name of the property; may not be null and must refer to a
* valid property name
* @return the interface for completing the value portion of the criteria
* specification; never null
*/
public ComparisonBuilder propertyValue(String table, String property);
/**
* Constrains the nodes in the the supplied table such that they must
* satisfy the supplied full-text search on the nodes' property values.
*
* @param table
* the name of the table; may not be null and must refer to a
* valid name or alias of a table appearing in the FROM clause
* @return the interface for completing the value portion of the criteria
* specification; never null
*/
public ComparisonBuilder fullTextSearchScore(String table);
/**
* Constrains the nodes in the the supplied table based upon criteria on
* the node's depth.
*
* @param table
* the name of the table; may not be null and must refer to a
* valid name or alias of a table appearing in the FROM clause
* @return the interface for completing the value portion of the criteria
* specification; never null
*/
public ComparisonBuilder depth(String table);
/**
* Constrains the nodes in the the supplied table based upon criteria on
* the node's local name.
*
* @param table
* the name of the table; may not be null and must refer to a
* valid name or alias of a table appearing in the FROM clause
* @return the interface for completing the value portion of the criteria
* specification; never null
*/
public ComparisonBuilder nodeLocalName(String table);
/**
* Constrains the nodes in the the supplied table based upon criteria on
* the node's name.
*
* @param table
* the name of the table; may not be null and must refer to a
* valid name or alias of a table appearing in the FROM clause
* @return the interface for completing the value portion of the criteria
* specification; never null
*/
public ComparisonBuilder nodeName(String table);
/**
* Begin a constraint against the uppercase form of a dynamic operand.
*
* @return the interface for completing the criteria specification; never
* null
*/
public DynamicOperandBuilder upperCaseOf();
/**
* Begin a constraint against the lowercase form of a dynamic operand.
*
* @return the interface for completing the criteria specification; never
* null
*/
public DynamicOperandBuilder lowerCaseOf();
}
public class ConstraintBuilder implements DynamicOperandBuilder
{
private final ConstraintBuilder parent;
/** Used for the current operations */
private Constraint constraint;
/** Set when a logical criteria is started */
private Constraint left;
private boolean and;
private boolean negateConstraint;
protected ConstraintBuilder(ConstraintBuilder parent)
{
this.parent = parent;
}
/**
* Complete this constraint specification.
*
* @return the query builder, for method chaining purposes
*/
public QueryBuilder end()
{
buildLogicalConstraint();
QueryBuilder.this.constraint = constraint;
return QueryBuilder.this;
}
/**
* Simulate the use of an open parenthesis in the constraint. The
* resulting builder should be used to define the constraint within the
* parenthesis, and should always be terminated with a
* {@link #closeParen()}.
*
* @return the constraint builder that should be used to define the
* portion of the constraint within the parenthesis; never null
* @see #closeParen()
*/
public ConstraintBuilder openParen()
{
return new ConstraintBuilder(this);
}
/**
* Complete the specification of a constraint clause, and return the
* builder for the parent constraint clause.
*
* @return the constraint builder that was used to create this
* parenthetical constraint clause builder; never null
* @throws IllegalStateException
* if there was not an {@link #openParen() open parenthesis} to
* close
*/
public ConstraintBuilder closeParen()
{
Validate.notNull(parent, "Unexpected parent == null");
buildLogicalConstraint();
return parent.setConstraint(constraint);
}
/**
* Signal that the previous constraint clause be AND-ed together with
* another constraint clause that will be defined immediately after this
* method call.
*
* @return the constraint builder for the remaining constraint clause;
* never null
*/
public ConstraintBuilder and()
{
buildLogicalConstraint();
left = constraint;
constraint = null;
and = true;
return this;
}
/**
* Signal that the previous constraint clause be OR-ed together with
* another constraint clause that will be defined immediately after this
* method call.
*
* @return the constraint builder for the remaining constraint clause;
* never null
*/
public ConstraintBuilder or()
{
buildLogicalConstraint();
left = constraint;
constraint = null;
and = false;
return this;
}
/**
* Signal that the next constraint clause (defined immediately after this
* method) should be negated.
*
* @return the constraint builder for the constraint clause that is to be
* negated; never null
*/
public ConstraintBuilder not()
{
negateConstraint = true;
return this;
}
protected ConstraintBuilder buildLogicalConstraint()
{
if (negateConstraint && constraint != null)
{
constraint = new Not(constraint);
negateConstraint = false;
}
if (left != null && constraint != null)
{
if (and)
{
// If the left constraint is an OR, we need to rearrange things
// since AND is higher precedence ...
if (left instanceof Or)
{
Or previous = (Or)left;
constraint = new Or(previous.getLeft(), new And(previous.getRight(), constraint));
}
else
{
constraint = new And(left, constraint);
}
}
else
{
constraint = new Or(left, constraint);
}
left = null;
}
return this;
}
/**
* Define a constraint clause that the node within the named table is the
* same node as that appearing at the supplied path.
*
* @param table
* the name of the table; may not be null and must refer to a
* valid name or alias of a table appearing in the FROM clause
* @param asNodeAtPath
* the path to the node
* @return the constraint builder that was used to create this clause;
* never null
*/
public ConstraintBuilder isSameNode(String table, String asNodeAtPath)
{
return setConstraint(new SameNode(selector(table), asNodeAtPath));
}
/**
* Define a constraint clause that the node within the named table is the
* child of the node at the supplied path.
*
* @param childTable
* the name of the table; may not be null and must refer to a
* valid name or alias of a table appearing in the FROM clause
* @param parentPath
* the path to the parent node
* @return the constraint builder that was used to create this clause;
* never null
*/
public ConstraintBuilder isChild(String childTable, String parentPath)
{
return setConstraint(new ChildNode(selector(childTable), parentPath));
}
/**
* Define a constraint clause that the node within the named table is a
* descendant of the node at the supplied path.
*
* @param descendantTable
* the name of the table; may not be null and must refer to a
* valid name or alias of a table appearing in the FROM clause
* @param ancestorPath
* the path to the ancestor node
* @return the constraint builder that was used to create this clause;
* never null
*/
public ConstraintBuilder isBelowPath(String descendantTable, String ancestorPath)
{
return setConstraint(new DescendantNode(selector(descendantTable), ancestorPath));
}
/**
* Define a constraint clause that the node within the named table has at
* least one value for the named property.
*
* @param table
* the name of the table; may not be null and must refer to a
* valid name or alias of a table appearing in the FROM clause
* @param propertyName
* the name of the property
* @return the constraint builder that was used to create this clause;
* never null
*/
public ConstraintBuilder hasProperty(String table, String propertyName)
{
return setConstraint(new PropertyExistence(selector(table), propertyName));
}
/**
* Define a constraint clause that the node within the named table have at
* least one property that satisfies the full-text search expression.
*
* @param table
* the name of the table; may not be null and must refer to a
* valid name or alias of a table appearing in the FROM clause
* @param searchExpression
* the full-text search expression
* @return the constraint builder that was used to create this clause;
* never null
*/
public ConstraintBuilder search(String table, String searchExpression)
{
return setConstraint(new FullTextSearch(selector(table), null, searchExpression));
}
/**
* Define a constraint clause that the node within the named table have a
* value for the named property that satisfies the full-text search
* expression.
*
* @param table
* the name of the table; may not be null and must refer to a
* valid name or alias of a table appearing in the FROM clause
* @param propertyName
* the name of the property to be searched
* @param searchExpression
* the full-text search expression
* @return the constraint builder that was used to create this clause;
* never null
*/
public ConstraintBuilder search(String table, String propertyName, String searchExpression)
{
return setConstraint(new FullTextSearch(selector(table), propertyName, searchExpression));
}
protected ComparisonBuilder comparisonBuilder(DynamicOperand operand)
{
return new ComparisonBuilder(this, operand);
}
/**
* {@inheritDoc}
*
* @see org.modeshape.graph.query.QueryBuilder.DynamicOperandBuilder#length(java.lang.String,
* java.lang.String)
*/
public ComparisonBuilder length(String table, String property)
{
return comparisonBuilder(new Length(new PropertyValue(selector(table), property)));
}
/**
* {@inheritDoc}
*
* @see org.modeshape.graph.query.QueryBuilder.DynamicOperandBuilder#propertyValue(String,
* String)
*/
public ComparisonBuilder propertyValue(String table, String property)
{
return comparisonBuilder(new PropertyValue(selector(table), property));
}
/**
* {@inheritDoc}
*
* @see org.modeshape.graph.query.QueryBuilder.DynamicOperandBuilder#fullTextSearchScore(String)
*/
public ComparisonBuilder fullTextSearchScore(String table)
{
return comparisonBuilder(new FullTextSearchScore(selector(table)));
}
/**
* {@inheritDoc}
*
* @see org.modeshape.graph.query.QueryBuilder.DynamicOperandBuilder#depth(java.lang.String)
*/
public ComparisonBuilder depth(String table)
{
return comparisonBuilder(new NodeDepth(selector(table)));
}
/**
* {@inheritDoc}
*
* @see org.modeshape.graph.query.QueryBuilder.DynamicOperandBuilder#nodeLocalName(String)
*/
public ComparisonBuilder nodeLocalName(String table)
{
return comparisonBuilder(new NodeLocalName(selector(table)));
}
/**
* {@inheritDoc}
*
* @see org.modeshape.graph.query.QueryBuilder.DynamicOperandBuilder#nodeName(String)
*/
public ComparisonBuilder nodeName(String table)
{
return comparisonBuilder(new NodeName(selector(table)));
}
/**
* {@inheritDoc}
*
* @see org.modeshape.graph.query.QueryBuilder.DynamicOperandBuilder#upperCaseOf()
*/
public DynamicOperandBuilder upperCaseOf()
{
return new UpperCaser(this);
}
/**
* {@inheritDoc}
*
* @see org.modeshape.graph.query.QueryBuilder.DynamicOperandBuilder#lowerCaseOf()
*/
public DynamicOperandBuilder lowerCaseOf()
{
return new LowerCaser(this);
}
protected ConstraintBuilder setConstraint(Constraint constraint)
{
if (this.constraint != null && this.left == null)
{
and();
}
this.constraint = constraint;
return buildLogicalConstraint();
}
}
/**
* A specialized form of the {@link ConstraintBuilder} that always wraps the
* generated constraint in a {@link UpperCase} instance.
*/
protected class UpperCaser extends ConstraintBuilder
{
private final ConstraintBuilder delegate;
protected UpperCaser(ConstraintBuilder delegate)
{
super(null);
this.delegate = delegate;
}
@Override
protected ConstraintBuilder setConstraint(Constraint constraint)
{
Comparison comparison = (Comparison)constraint;
return delegate.setConstraint(new Comparison(new UpperCase(comparison.getOperand1()),
comparison.getOperator(), comparison.getOperand2()));
}
}
/**
* A specialized form of the {@link ConstraintBuilder} that always wraps the
* generated constraint in a {@link LowerCase} instance.
*/
protected class LowerCaser extends ConstraintBuilder
{
private final ConstraintBuilder delegate;
protected LowerCaser(ConstraintBuilder delegate)
{
super(null);
this.delegate = delegate;
}
@Override
protected ConstraintBuilder setConstraint(Constraint constraint)
{
Comparison comparison = (Comparison)constraint;
return delegate.setConstraint(new Comparison(new LowerCase(comparison.getOperand1()),
comparison.getOperator(), comparison.getOperand2()));
}
}
public abstract class CastAs<ReturnType>
{
protected final Object value;
protected CastAs(Object value)
{
this.value = value;
}
/**
* Define the right-hand side literal value cast as the specified type.
*
* @param type
* the property type; may not be null
* @return the constraint builder; never null
*/
public abstract ReturnType as(PropertyType type);
/**
* Define the right-hand side literal value cast as a
* {@link PropertyType#STRING}.
*
* @return the constraint builder; never null
*/
public ReturnType asString()
{
return as(PropertyType.STRING);
}
/**
* Define the right-hand side literal value cast as a
* {@link PropertyType#BOOLEAN}.
*
* @return the constraint builder; never null
*/
public ReturnType asBoolean()
{
return as(PropertyType.BOOLEAN);
}
/**
* Define the right-hand side literal value cast as a
* {@link PropertyType#LONG}.
*
* @return the constraint builder; never null
*/
public ReturnType asLong()
{
return as(PropertyType.LONG);
}
/**
* Define the right-hand side literal value cast as a
* {@link PropertyType#DOUBLE}.
*
* @return the constraint builder; never null
*/
public ReturnType asDouble()
{
return as(PropertyType.DOUBLE);
}
/**
* Define the right-hand side literal value cast as a
* {@link PropertyType#DATE}.
*
* @return the constraint builder; never null
*/
public ReturnType asDate()
{
return as(PropertyType.DATE);
}
/**
* Define the right-hand side literal value cast as a
* {@link PropertyType#PATH}.
*
* @return the constraint builder; never null
*/
public ReturnType asPath()
{
return as(PropertyType.PATH);
}
}
public class CastAsRightHandSide extends CastAs<ConstraintBuilder>
{
private final RightHandSide rhs;
protected CastAsRightHandSide(RightHandSide rhs, Object value)
{
super(value);
this.rhs = rhs;
}
/**
* Define the right-hand side literal value cast as the specified type.
*
* @param type
* the property type; may not be null
* @return the constraint builder; never null
*/
@Override
public ConstraintBuilder as(PropertyType type)
{
return rhs.comparisonBuilder.is(rhs.operator, castSystem.cast(value, type));
}
}
public class RightHandSide
{
protected final Operator operator;
protected final ComparisonBuilder comparisonBuilder;
protected RightHandSide(ComparisonBuilder comparisonBuilder, Operator operator)
{
this.operator = operator;
this.comparisonBuilder = comparisonBuilder;
}
/**
* Define the right-hand side of a comparison.
*
* @param literal
* the literal value;
* @return the constraint builder; never null
*/
public ConstraintBuilder literal(String literal)
{
return comparisonBuilder.is(operator, literal);
}
/**
* Define the right-hand side of a comparison.
*
* @param literal
* the literal value;
* @return the constraint builder; never null
*/
public ConstraintBuilder literal(int literal)
{
return comparisonBuilder.is(operator, literal);
}
/**
* Define the right-hand side of a comparison.
*
* @param literal
* the literal value;
* @return the constraint builder; never null
*/
public ConstraintBuilder literal(long literal)
{
return comparisonBuilder.is(operator, literal);
}
/**
* Define the right-hand side of a comparison.
*
* @param literal
* the literal value;
* @return the constraint builder; never null
*/
public ConstraintBuilder literal(float literal)
{
return comparisonBuilder.is(operator, literal);
}
/**
* Define the right-hand side of a comparison.
*
* @param literal
* the literal value;
* @return the constraint builder; never null
*/
public ConstraintBuilder literal(double literal)
{
return comparisonBuilder.is(operator, literal);
}
/**
* Define the right-hand side of a comparison.
*
* @param literal
* the literal value;
* @return the constraint builder; never null
*/
public ConstraintBuilder literal(Calendar literal)
{
return comparisonBuilder.is(operator, literal.getTimeInMillis());
}
/**
* Define the right-hand side of a comparison.
*
* @param literal
* the literal value;
* @return the constraint builder; never null
*/
public ConstraintBuilder literal(URI literal)
{
return comparisonBuilder.is(operator, literal);
}
/**
* Define the right-hand side of a comparison.
*
* @param literal
* the literal value;
* @return the constraint builder; never null
*/
public ConstraintBuilder literal(UUID literal)
{
return comparisonBuilder.is(operator, literal);
}
/**
* Define the right-hand side of a comparison.
*
* @param literal
* the literal value;
* @return the constraint builder; never null
*/
public ConstraintBuilder literal(BigDecimal literal)
{
return comparisonBuilder.is(operator, literal);
}
/**
* Define the right-hand side of a comparison.
*
* @param literal
* the literal value;
* @return the constraint builder; never null
*/
public ConstraintBuilder literal(boolean literal)
{
return comparisonBuilder.is(operator, literal);
}
/**
* Define the right-hand side of a comparison.
*
* @param variableName
* the name of the variable
* @return the constraint builder; never null
*/
public ConstraintBuilder variable(String variableName)
{
return comparisonBuilder.is(operator, variableName);
}
/**
* Define the right-hand side of a comparison.
*
* @param literal
* the literal value that is to be cast
* @return the constraint builder; never null
*/
public CastAs<ConstraintBuilder> cast(int literal)
{
return new CastAsRightHandSide(this, literal);
}
/**
* Define the right-hand side of a comparison.
*
* @param literal
* the literal value that is to be cast
* @return the constraint builder; never null
*/
public CastAs<ConstraintBuilder> cast(String literal)
{
return new CastAsRightHandSide(this, literal);
}
/**
* Define the right-hand side of a comparison.
*
* @param literal
* the literal value that is to be cast
* @return the constraint builder; never null
*/
public CastAs<ConstraintBuilder> cast(boolean literal)
{
return new CastAsRightHandSide(this, literal);
}
/**
* Define the right-hand side of a comparison.
*
* @param literal
* the literal value that is to be cast
* @return the constraint builder; never null
*/
public CastAs<ConstraintBuilder> cast(long literal)
{
return new CastAsRightHandSide(this, literal);
}
/**
* Define the right-hand side of a comparison.
*
* @param literal
* the literal value that is to be cast
* @return the constraint builder; never null
*/
public CastAs<ConstraintBuilder> cast(double literal)
{
return new CastAsRightHandSide(this, literal);
}
/**
* Define the right-hand side of a comparison.
*
* @param literal
* the literal value that is to be cast
* @return the constraint builder; never null
*/
public CastAs<ConstraintBuilder> cast(BigDecimal literal)
{
return new CastAsRightHandSide(this, literal);
}
/**
* Define the right-hand side of a comparison.
*
* @param literal
* the literal value that is to be cast
* @return the constraint builder; never null
*/
public CastAs<ConstraintBuilder> cast(Calendar literal)
{
return new CastAsRightHandSide(this, literal.getTimeInMillis());
}
/**
* Define the right-hand side of a comparison.
*
* @param literal
* the literal value that is to be cast
* @return the constraint builder; never null
*/
public CastAs<ConstraintBuilder> cast(UUID literal)
{
return new CastAsRightHandSide(this, literal);
}
/**
* Define the right-hand side of a comparison.
*
* @param literal
* the literal value that is to be cast
* @return the constraint builder; never null
*/
public CastAs<ConstraintBuilder> cast(URI literal)
{
return new CastAsRightHandSide(this, literal);
}
}
/**
* An interface used to set the right-hand side of a constraint.
*/
public class ComparisonBuilder
{
protected final DynamicOperand left;
protected final ConstraintBuilder constraintBuilder;
protected ComparisonBuilder(ConstraintBuilder constraintBuilder, DynamicOperand left)
{
this.left = left;
this.constraintBuilder = constraintBuilder;
}
/**
* Define the operator that will be used in the comparison, returning an
* interface that can be used to define the right-hand-side of the
* comparison.
*
* @param operator
* the operator; may not be null
* @return the interface used to define the right-hand-side of the
* comparison
*/
public RightHandSide is(Operator operator)
{
Validate.notNull(operator, "The operator argument may not be null");
return new RightHandSide(this, operator);
}
/**
* Use the 'equal to' operator in the comparison, returning an interface
* that can be used to define the right-hand-side of the comparison.
*
* @return the interface used to define the right-hand-side of the
* comparison
*/
public RightHandSide isEqualTo()
{
return is(Operator.EQUAL_TO);
}
/**
* Use the 'equal to' operator in the comparison, returning an interface
* that can be used to define the right-hand-side of the comparison.
*
* @return the interface used to define the right-hand-side of the
* comparison
*/
public RightHandSide isNotEqualTo()
{
return is(Operator.NOT_EQUAL_TO);
}
/**
* Use the 'equal to' operator in the comparison, returning an interface
* that can be used to define the right-hand-side of the comparison.
*
* @return the interface used to define the right-hand-side of the
* comparison
*/
public RightHandSide isGreaterThan()
{
return is(Operator.GREATER_THAN);
}
/**
* Use the 'equal to' operator in the comparison, returning an interface
* that can be used to define the right-hand-side of the comparison.
*
* @return the interface used to define the right-hand-side of the
* comparison
*/
public RightHandSide isGreaterThanOrEqualTo()
{
return is(Operator.GREATER_THAN_OR_EQUAL_TO);
}
/**
* Use the 'equal to' operator in the comparison, returning an interface
* that can be used to define the right-hand-side of the comparison.
*
* @return the interface used to define the right-hand-side of the
* comparison
*/
public RightHandSide isLessThan()
{
return is(Operator.LESS_THAN);
}
/**
* Use the 'equal to' operator in the comparison, returning an interface
* that can be used to define the right-hand-side of the comparison.
*
* @return the interface used to define the right-hand-side of the
* comparison
*/
public RightHandSide isLessThanOrEqualTo()
{
return is(Operator.LESS_THAN_OR_EQUAL_TO);
}
/**
* Use the 'equal to' operator in the comparison, returning an interface
* that can be used to define the right-hand-side of the comparison.
*
* @return the interface used to define the right-hand-side of the
* comparison
*/
public RightHandSide isLike()
{
return is(Operator.LIKE);
}
/**
* Define the right-hand-side of the constraint using the supplied
* operator.
*
* @param operator
* the operator; may not be null
* @param variableName
* the name of the variable
* @return the builder used to create the constraint clause, ready to be
* used to create other constraints clauses or complete
* already-started clauses; never null
*/
public ConstraintBuilder isVariable(Operator operator, String variableName)
{
Validate.notNull(operator, "The operator argument may not be null");
return this.constraintBuilder
.setConstraint(new Comparison(left, operator, new BindVariableName(variableName)));
}
/**
* Define the right-hand-side of the constraint using the supplied
* operator.
*
* @param operator
* the operator; may not be null
* @param literal
* the literal value
* @return the builder used to create the constraint clause, ready to be
* used to create other constraints clauses or complete
* already-started clauses; never null
*/
public ConstraintBuilder is(Operator operator, Object literal)
{
assert operator != null;
Literal value = literal instanceof Literal ? (Literal)literal : new Literal(literal);
return this.constraintBuilder.setConstraint(new Comparison(left, operator, value));
}
/**
* Define the right-hand-side of the constraint to be equivalent to the
* value of the supplied variable.
*
* @param variableName
* the name of the variable
* @return the builder used to create the constraint clause, ready to be
* used to create other constraints clauses or complete
* already-started clauses; never null
*/
public ConstraintBuilder isEqualToVariable(String variableName)
{
return isVariable(Operator.EQUAL_TO, variableName);
}
/**
* Define the right-hand-side of the constraint to be greater than the
* value of the supplied variable.
*
* @param variableName
* the name of the variable
* @return the builder used to create the constraint clause, ready to be
* used to create other constraints clauses or complete
* already-started clauses; never null
*/
public ConstraintBuilder isGreaterThanVariable(String variableName)
{
return isVariable(Operator.GREATER_THAN, variableName);
}
/**
* Define the right-hand-side of the constraint to be greater than or
* equal to the value of the supplied variable.
*
* @param variableName
* the name of the variable
* @return the builder used to create the constraint clause, ready to be
* used to create other constraints clauses or complete
* already-started clauses; never null
*/
public ConstraintBuilder isGreaterThanOrEqualToVariable(String variableName)
{
return isVariable(Operator.GREATER_THAN_OR_EQUAL_TO, variableName);
}
/**
* Define the right-hand-side of the constraint to be less than the value
* of the supplied variable.
*
* @param variableName
* the name of the variable
* @return the builder used to create the constraint clause, ready to be
* used to create other constraints clauses or complete
* already-started clauses; never null
*/
public ConstraintBuilder isLessThanVariable(String variableName)
{
return isVariable(Operator.LESS_THAN, variableName);
}
/**
* Define the right-hand-side of the constraint to be less than or equal
* to the value of the supplied variable.
*
* @param variableName
* the name of the variable
* @return the builder used to create the constraint clause, ready to be
* used to create other constraints clauses or complete
* already-started clauses; never null
*/
public ConstraintBuilder isLessThanOrEqualToVariable(String variableName)
{
return isVariable(Operator.LESS_THAN_OR_EQUAL_TO, variableName);
}
/**
* Define the right-hand-side of the constraint to be LIKE the value of
* the supplied variable.
*
* @param variableName
* the name of the variable
* @return the builder used to create the constraint clause, ready to be
* used to create other constraints clauses or complete
* already-started clauses; never null
*/
public ConstraintBuilder isLikeVariable(String variableName)
{
return isVariable(Operator.LIKE, variableName);
}
/**
* Define the right-hand-side of the constraint to be not equal to the
* value of the supplied variable.
*
* @param variableName
* the name of the variable
* @return the builder used to create the constraint clause, ready to be
* used to create other constraints clauses or complete
* already-started clauses; never null
*/
public ConstraintBuilder isNotEqualToVariable(String variableName)
{
return isVariable(Operator.NOT_EQUAL_TO, variableName);
}
/**
* Define the right-hand-side of the constraint to be equivalent to the
* supplied literal value.
*
* @param literal
* the literal value
* @return the builder used to create the constraint clause, ready to be
* used to create other constraints clauses or complete
* already-started clauses; never null
*/
public ConstraintBuilder isEqualTo(Object literal)
{
return is(Operator.EQUAL_TO, literal);
}
/**
* Define the right-hand-side of the constraint to be greater than the
* supplied literal value.
*
* @param literal
* the literal value
* @return the builder used to create the constraint clause, ready to be
* used to create other constraints clauses or complete
* already-started clauses; never null
*/
public ConstraintBuilder isGreaterThan(Object literal)
{
return is(Operator.GREATER_THAN, literal);
}
/**
* Define the right-hand-side of the constraint to be greater than or
* equal to the supplied literal value.
*
* @param literal
* the literal value
* @return the builder used to create the constraint clause, ready to be
* used to create other constraints clauses or complete
* already-started clauses; never null
*/
public ConstraintBuilder isGreaterThanOrEqualTo(Object literal)
{
return is(Operator.GREATER_THAN_OR_EQUAL_TO, literal);
}
/**
* Define the right-hand-side of the constraint to be less than the
* supplied literal value.
*
* @param literal
* the literal value
* @return the builder used to create the constraint clause, ready to be
* used to create other constraints clauses or complete
* already-started clauses; never null
*/
public ConstraintBuilder isLessThan(Object literal)
{
return is(Operator.LESS_THAN, literal);
}
/**
* Define the right-hand-side of the constraint to be less than or equal
* to the supplied literal value.
*
* @param literal
* the literal value
* @return the builder used to create the constraint clause, ready to be
* used to create other constraints clauses or complete
* already-started clauses; never null
*/
public ConstraintBuilder isLessThanOrEqualTo(Object literal)
{
return is(Operator.LESS_THAN_OR_EQUAL_TO, literal);
}
/**
* Define the right-hand-side of the constraint to be LIKE the supplied
* literal value.
*
* @param literal
* the literal value
* @return the builder used to create the constraint clause, ready to be
* used to create other constraints clauses or complete
* already-started clauses; never null
*/
public ConstraintBuilder isLike(Object literal)
{
return is(Operator.LIKE, literal);
}
/**
* Define the right-hand-side of the constraint to be not equal to the
* supplied literal value.
*
* @param literal
* the literal value
* @return the builder used to create the constraint clause, ready to be
* used to create other constraints clauses or complete
* already-started clauses; never null
*/
public ConstraintBuilder isNotEqualTo(Object literal)
{
return is(Operator.NOT_EQUAL_TO, literal);
}
}
public class AndBuilder<T>
{
private final T object;
protected AndBuilder(T object)
{
assert object != null;
this.object = object;
}
/**
* Return the component
*
* @return the component; never null
*/
public T and()
{
return this.object;
}
}
}