You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@metamodel.apache.org by ka...@apache.org on 2013/07/22 10:10:58 UTC
[45/64] [partial] Hard rename of all 'org/eobjects' folders to
'org/apache'.
http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/query/FilterItem.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/FilterItem.java b/core/src/main/java/org/apache/metamodel/query/FilterItem.java
new file mode 100644
index 0000000..86fdd20
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/query/FilterItem.java
@@ -0,0 +1,542 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.eobjects.metamodel.query;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eobjects.metamodel.data.IRowFilter;
+import org.eobjects.metamodel.data.Row;
+import org.eobjects.metamodel.schema.Column;
+import org.eobjects.metamodel.schema.ColumnType;
+import org.eobjects.metamodel.util.BaseObject;
+import org.eobjects.metamodel.util.CollectionUtils;
+import org.eobjects.metamodel.util.FormatHelper;
+import org.eobjects.metamodel.util.ObjectComparator;
+import org.eobjects.metamodel.util.WildcardPattern;
+
+/**
+ * Represents a filter in a query that resides either within a WHERE clause or a
+ * HAVING clause
+ *
+ * @see FilterClause
+ * @see OperatorType
+ * @see LogicalOperator
+ */
+public class FilterItem extends BaseObject implements QueryItem, Cloneable, IRowFilter {
+
+ private static final long serialVersionUID = 2435322742894653227L;
+
+ private Query _query;
+ private final SelectItem _selectItem;
+ private final OperatorType _operator;
+ private final Object _operand;
+ private final List<FilterItem> _childItems;
+ private final LogicalOperator _logicalOperator;
+ private final String _expression;
+ private transient Set<?> _inValues;
+
+ /**
+ * Private constructor, used for cloning
+ */
+ private FilterItem(SelectItem selectItem, OperatorType operator, Object operand, List<FilterItem> orItems,
+ String expression, LogicalOperator logicalOperator) {
+ _selectItem = selectItem;
+ _operator = operator;
+ _operand = validateOperand(operand);
+ _childItems = orItems;
+ _expression = expression;
+ _logicalOperator = logicalOperator;
+ }
+
+ private Object validateOperand(Object operand) {
+ if (operand instanceof Column) {
+ // gracefully convert to a select item.
+ operand = new SelectItem((Column) operand);
+ }
+ return operand;
+ }
+
+ /**
+ * Creates a single filter item based on a SelectItem, an operator and an
+ * operand.
+ *
+ * @param selectItem
+ * the selectItem to put constraints on, cannot be null
+ * @param operator
+ * The operator to use. Can be OperatorType.EQUALS_TO,
+ * OperatorType.DIFFERENT_FROM,
+ * OperatorType.GREATER_THAN,OperatorType.LESS_THAN
+ * @param operand
+ * The operand. Can be a constant like null or a String, a
+ * Number, a Boolean, a Date, a Time, a DateTime. Or another
+ * SelectItem
+ * @throws IllegalArgumentException
+ * if the SelectItem is null or if the combination of operator
+ * and operand does not make sense.
+ */
+ public FilterItem(SelectItem selectItem, OperatorType operator, Object operand) throws IllegalArgumentException {
+ this(selectItem, operator, operand, null, null, null);
+ if (_operand == null) {
+ require("Can only use EQUALS or DIFFERENT_FROM operator with null-operand",
+ _operator == OperatorType.DIFFERENT_FROM || _operator == OperatorType.EQUALS_TO);
+ }
+ if (_operator == OperatorType.LIKE) {
+ ColumnType type = _selectItem.getColumn().getType();
+ if (type != null) {
+ require("Can only use LIKE operator with strings", type.isLiteral()
+ && (_operand instanceof String || _operand instanceof SelectItem));
+ }
+ }
+ require("SelectItem cannot be null", _selectItem != null);
+ }
+
+ /**
+ * Creates a single unvalidated filter item based on a expression.
+ * Expression based filters are typically NOT datastore-neutral but are
+ * available for special "hacking" needs.
+ *
+ * Expression based filters can only be used for JDBC based datastores since
+ * they are translated directly into SQL.
+ *
+ * @param expression
+ * An expression to use for the filter, for example
+ * "YEAR(my_date) = 2008".
+ */
+ public FilterItem(String expression) {
+ this(null, null, null, null, expression, null);
+
+ require("Expression cannot be null", _expression != null);
+ }
+
+ /**
+ * Creates a composite filter item based on other filter items. Each
+ * provided filter items will be OR'ed meaning that if one of the evaluates
+ * as true, then the composite filter will be evaluated as true
+ *
+ * @param items
+ * a list of items to include in the composite
+ */
+ public FilterItem(List<FilterItem> items) {
+ this(LogicalOperator.OR, items);
+ }
+
+ /**
+ * Creates a compound filter item based on other filter items. Each provided
+ * filter item will be combined according to the {@link LogicalOperator}.
+ *
+ * @param logicalOperator
+ * the logical operator to apply
+ * @param items
+ * a list of items to include in the composite
+ */
+ public FilterItem(LogicalOperator logicalOperator, List<FilterItem> items) {
+ this(null, null, null, items, null, logicalOperator);
+
+ require("Child items cannot be null", _childItems != null);
+ require("Child items cannot be empty", !_childItems.isEmpty());
+ }
+
+ /**
+ * Creates a compound filter item based on other filter items. Each provided
+ * filter item will be combined according to the {@link LogicalOperator}.
+ *
+ * @param logicalOperator
+ * the logical operator to apply
+ * @param items
+ * an array of items to include in the composite
+ */
+ public FilterItem(LogicalOperator logicalOperator, FilterItem... items) {
+ this(logicalOperator, Arrays.asList(items));
+ }
+
+ /**
+ * Creates a compound filter item based on other filter items. Each provided
+ * filter items will be OR'ed meaning that if one of the evaluates as true,
+ * then the compound filter will be evaluated as true
+ *
+ * @param items
+ * an array of items to include in the composite
+ */
+ public FilterItem(FilterItem... items) {
+ this(Arrays.asList(items));
+ }
+
+ private void require(String errorMessage, boolean b) {
+ if (!b) {
+ throw new IllegalArgumentException(errorMessage);
+ }
+ }
+
+ public SelectItem getSelectItem() {
+ return _selectItem;
+ }
+
+ public OperatorType getOperator() {
+ return _operator;
+ }
+
+ public Object getOperand() {
+ return _operand;
+ }
+
+ public String getExpression() {
+ return _expression;
+ }
+
+ public Query getQuery() {
+ return _query;
+ }
+
+ public LogicalOperator getLogicalOperator() {
+ return _logicalOperator;
+ }
+
+ public FilterItem setQuery(Query query) {
+ _query = query;
+ if (_childItems == null) {
+ if (_expression == null) {
+ if (_selectItem.getQuery() == null) {
+ _selectItem.setQuery(_query);
+ }
+ if (_operand instanceof SelectItem) {
+ SelectItem operand = (SelectItem) _operand;
+ if (operand.getQuery() == null) {
+ operand.setQuery(_query);
+ }
+ }
+ }
+ } else {
+ for (FilterItem item : _childItems) {
+ if (item.getQuery() == null) {
+ item.setQuery(_query);
+ }
+ }
+ }
+ return this;
+ }
+
+ @Override
+ public String toSql() {
+ return toSql(false);
+ }
+
+ /**
+ * Parses the constraint as a SQL Where-clause item
+ */
+ @Override
+ public String toSql(boolean includeSchemaInColumnPaths) {
+ if (_expression != null) {
+ return _expression;
+ }
+
+ StringBuilder sb = new StringBuilder();
+ if (_childItems == null) {
+ sb.append(_selectItem.getSameQueryAlias(includeSchemaInColumnPaths));
+
+ if (_operand == null && _operator == OperatorType.EQUALS_TO) {
+ sb.append(" IS NULL");
+ } else if (_operand == null && _operator == OperatorType.DIFFERENT_FROM) {
+ sb.append(" IS NOT NULL");
+ } else {
+ final Object operand = appendOperator(sb, _operand, _operator);
+
+ if (operand instanceof SelectItem) {
+ final String selectItemString = ((SelectItem) operand).getSameQueryAlias(includeSchemaInColumnPaths);
+ sb.append(selectItemString);
+ } else {
+ ColumnType columnType = _selectItem.getExpectedColumnType();
+ final String sqlValue = FormatHelper.formatSqlValue(columnType, operand);
+ sb.append(sqlValue);
+ }
+ }
+ } else {
+ sb.append('(');
+ for (int i = 0; i < _childItems.size(); i++) {
+ FilterItem item = _childItems.get(i);
+ if (i != 0) {
+ sb.append(' ');
+ sb.append(_logicalOperator.toString());
+ sb.append(' ');
+ }
+ sb.append(item.toSql());
+ }
+ sb.append(')');
+ }
+
+ return sb.toString();
+ }
+
+ @SuppressWarnings("deprecation")
+ public static Object appendOperator(StringBuilder sb, Object operand, OperatorType operator) {
+ switch (operator) {
+ case DIFFERENT_FROM:
+ sb.append(" <> ");
+ break;
+ case EQUALS_TO:
+ sb.append(" = ");
+ break;
+ case LIKE:
+ sb.append(" LIKE ");
+ break;
+ case GREATER_THAN:
+ case HIGHER_THAN:
+ sb.append(" > ");
+ break;
+ case LESS_THAN:
+ case LOWER_THAN:
+ sb.append(" < ");
+ break;
+ case IN:
+ sb.append(" IN ");
+ operand = CollectionUtils.toList(operand);
+ break;
+ default:
+ throw new IllegalStateException("Operator could not be determined");
+ }
+ return operand;
+ }
+
+ /**
+ * Does a "manual" evaluation, useful for CSV data and alike, where queries
+ * cannot be created.
+ */
+ public boolean evaluate(Row row) {
+ require("Expression-based filters cannot be manually evaluated", _expression == null);
+
+ if (_childItems == null) {
+ // Evaluate a single constraint
+ Object selectItemValue = row.getValue(_selectItem);
+ Object operandValue = _operand;
+ if (_operand instanceof SelectItem) {
+ SelectItem selectItem = (SelectItem) _operand;
+ operandValue = row.getValue(selectItem);
+ }
+ if (operandValue == null) {
+ if (_operator == OperatorType.DIFFERENT_FROM) {
+ return (selectItemValue != null);
+ } else if (_operator == OperatorType.EQUALS_TO) {
+ return (selectItemValue == null);
+ } else {
+ return false;
+ }
+ } else if (selectItemValue == null) {
+ if (_operator == OperatorType.DIFFERENT_FROM) {
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return compare(selectItemValue, operandValue);
+ }
+ } else {
+
+ // Evaluate several constraints
+ if (_logicalOperator == LogicalOperator.AND) {
+ // require all results to be true
+ for (FilterItem item : _childItems) {
+ boolean result = item.evaluate(row);
+ if (!result) {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ // require at least one result to be true
+ for (FilterItem item : _childItems) {
+ boolean result = item.evaluate(row);
+ if (result) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ private boolean compare(Object selectItemValue, Object operandValue) {
+ Comparator<Object> comparator = ObjectComparator.getComparator();
+ if (_operator == OperatorType.DIFFERENT_FROM) {
+ return comparator.compare(selectItemValue, operandValue) != 0;
+ } else if (_operator == OperatorType.EQUALS_TO) {
+ return comparator.compare(selectItemValue, operandValue) == 0;
+ } else if (_operator == OperatorType.GREATER_THAN || _operator == OperatorType.HIGHER_THAN) {
+ return comparator.compare(selectItemValue, operandValue) > 0;
+ } else if (_operator == OperatorType.LESS_THAN || _operator == OperatorType.LOWER_THAN) {
+ return comparator.compare(selectItemValue, operandValue) < 0;
+ } else if (_operator == OperatorType.LIKE) {
+ WildcardPattern matcher = new WildcardPattern((String) operandValue, '%');
+ return matcher.matches((String) selectItemValue);
+ } else if (_operator == OperatorType.IN) {
+ Set<?> inValues = getInValues();
+ return inValues.contains(selectItemValue);
+ } else {
+ throw new IllegalStateException("Operator could not be determined");
+ }
+ }
+
+ /**
+ * Lazy initializes a set (for fast searching) of IN values.
+ *
+ * @return a hash set appropriate for IN clause evaluation
+ */
+ private Set<?> getInValues() {
+ if (_inValues == null) {
+ if (_operand instanceof Set) {
+ _inValues = (Set<?>) _operand;
+ } else {
+ List<?> list = CollectionUtils.toList(_operand);
+ _inValues = new HashSet<Object>(list);
+ }
+ }
+ return _inValues;
+ }
+
+ @Override
+ protected FilterItem clone() {
+ final List<FilterItem> orItems;
+ if (_childItems == null) {
+ orItems = null;
+ } else {
+ orItems = new ArrayList<FilterItem>(_childItems);
+ }
+
+ final Object operand;
+ if (_operand instanceof SelectItem) {
+ operand = ((SelectItem) _operand).clone();
+ } else {
+ operand = _operand;
+ }
+
+ final SelectItem selectItem;
+ if (_selectItem == null) {
+ selectItem = null;
+ } else {
+ selectItem = _selectItem.clone();
+ }
+
+ return new FilterItem(selectItem, _operator, operand, orItems, _expression, _logicalOperator);
+ }
+
+ public boolean isReferenced(Column column) {
+ if (column != null) {
+ if (_selectItem != null) {
+ if (_selectItem.isReferenced(column)) {
+ return true;
+ }
+ }
+ if (_operand != null && _operand instanceof SelectItem) {
+ if (((SelectItem) _operand).isReferenced(column)) {
+ return true;
+ }
+ }
+ if (_childItems != null) {
+ for (FilterItem item : _childItems) {
+ if (item.isReferenced(column)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ protected void decorateIdentity(List<Object> identifiers) {
+ identifiers.add(_expression);
+ identifiers.add(_operand);
+ identifiers.add(_childItems);
+ identifiers.add(_operator);
+ identifiers.add(_selectItem);
+ identifiers.add(_logicalOperator);
+ }
+
+ /**
+ * Gets the {@link FilterItem}s that this filter item consists of, if it is
+ * a compound filter item.
+ *
+ * @return
+ * @deprecated use {@link #getChildItems()} instead
+ */
+ @Deprecated
+ public FilterItem[] getOrItems() {
+ return getChildItems();
+ }
+
+ /**
+ * Gets the number of child items, if this is a compound filter item.
+ *
+ * @return
+ * @deprecated use {@link #getChildItemCount()} instead.
+ */
+ @Deprecated
+ public int getOrItemCount() {
+ return getChildItemCount();
+ }
+
+ /**
+ * Get the number of child items, if this is a compound filter item.
+ *
+ * @return
+ */
+ public int getChildItemCount() {
+ if (_childItems == null) {
+ return 0;
+ }
+ return _childItems.size();
+ }
+
+ /**
+ * Gets the {@link FilterItem}s that this filter item consists of, if it is
+ * a compound filter item.
+ *
+ * @return
+ */
+ public FilterItem[] getChildItems() {
+ if (_childItems == null) {
+ return null;
+ }
+ return _childItems.toArray(new FilterItem[_childItems.size()]);
+ }
+
+ /**
+ * Determines whether this {@link FilterItem} is a compound filter or not
+ * (ie. if it has child items or not)
+ *
+ * @return
+ */
+ public boolean isCompoundFilter() {
+ return _childItems != null;
+ }
+
+ @Override
+ public String toString() {
+ return toSql();
+ }
+
+ @Override
+ public boolean accept(Row row) {
+ return evaluate(row);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/query/FromClause.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/FromClause.java b/core/src/main/java/org/apache/metamodel/query/FromClause.java
new file mode 100644
index 0000000..950c246
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/query/FromClause.java
@@ -0,0 +1,103 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.eobjects.metamodel.query;
+
+import org.eobjects.metamodel.schema.Table;
+
+/**
+ * Represents the FROM clause of a query containing FromItem's.
+ *
+ * @see FromItem
+ */
+public class FromClause extends AbstractQueryClause<FromItem> {
+
+ private static final long serialVersionUID = -8227310702249122115L;
+
+ public FromClause(Query query) {
+ super(query, AbstractQueryClause.PREFIX_FROM, AbstractQueryClause.DELIM_COMMA);
+ }
+
+ /**
+ * Gets the alias of a table, if it is registered (and visible, ie. not part
+ * of a sub-query) in the FromClause
+ *
+ * @param table
+ * the table to get the alias for
+ * @return the alias or null if none is found
+ */
+ public String getAlias(Table table) {
+ if (table != null) {
+ for (FromItem item : getItems()) {
+ String alias = item.getAlias(table);
+ if (alias != null) {
+ return alias;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Retrieves a table by it's reference which may be it's alias or it's
+ * qualified table name. Typically, this method is used to resolve a
+ * SelectItem with a reference like "foo.bar", where "foo" may either be an
+ * alias or a table name
+ *
+ * @param reference
+ * @return a FromItem which matches the provided reference string
+ */
+ public FromItem getItemByReference(String reference) {
+ if (reference == null) {
+ return null;
+ }
+ for (final FromItem item : getItems()) {
+ FromItem result = getItemByReference(item, reference);
+ if (result != null) {
+ return result;
+ }
+ }
+ return null;
+ }
+
+ private FromItem getItemByReference(FromItem item, String reference) {
+ final String alias = item.getAlias();
+ if (reference.equals(alias)) {
+ return item;
+ }
+
+ final Table table = item.getTable();
+ if (alias == null && table != null && reference.equals(table.getName())) {
+ return item;
+ }
+
+ final JoinType join = item.getJoin();
+ if (join != null) {
+ final FromItem leftResult = getItemByReference(item.getLeftSide(), reference);
+ if (leftResult != null) {
+ return leftResult;
+ }
+ final FromItem rightResult = getItemByReference(item.getRightSide(), reference);
+ if (rightResult != null) {
+ return rightResult;
+ }
+ }
+
+ return null;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/query/FromItem.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/FromItem.java b/core/src/main/java/org/apache/metamodel/query/FromItem.java
new file mode 100644
index 0000000..2ea22bd
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/query/FromItem.java
@@ -0,0 +1,349 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.eobjects.metamodel.query;
+
+import java.util.List;
+
+import org.eobjects.metamodel.schema.Column;
+import org.eobjects.metamodel.schema.Relationship;
+import org.eobjects.metamodel.schema.Table;
+import org.eobjects.metamodel.util.BaseObject;
+
+/**
+ * Represents a FROM item. FROM items can take different forms:
+ * <ul>
+ * <li>table FROMs (eg. "FROM products p")</li>
+ * <lI>join FROMs with an ON clause (eg. "FROM products p INNER JOIN orders o ON
+ * p.id = o.product_id")</li>
+ * <li>subquery FROMs (eg. "FROM (SELECT * FROM products) p")</li>
+ * <li>expression FROM (any string based from item)</li>
+ * </ul>
+ *
+ * @see FromClause
+ */
+public class FromItem extends BaseObject implements QueryItem, Cloneable {
+
+ private static final long serialVersionUID = -6559220014058975193L;
+ private Table _table;
+ private String _alias;
+ private Query _subQuery;
+ private JoinType _join;
+ private FromItem _leftSide;
+ private FromItem _rightSide;
+ private SelectItem[] _leftOn;
+ private SelectItem[] _rightOn;
+ private Query _query;
+ private String _expression;
+
+ /**
+ * Private constructor, used for cloning
+ */
+ private FromItem() {
+ }
+
+ /**
+ * Constructor for table FROM clauses
+ */
+ public FromItem(Table table) {
+ _table = table;
+ }
+
+ /**
+ * Constructor for sub-query FROM clauses
+ *
+ * @param subQuery
+ * the subquery to use
+ */
+ public FromItem(Query subQuery) {
+ _subQuery = subQuery;
+ }
+
+ /**
+ * Constructor for join FROM clauses that join two tables using their
+ * relationship. The primary table of the relationship will be the left side
+ * of the join and the foreign table of the relationship will be the right
+ * side of the join.
+ *
+ * @param join
+ * the join type to use
+ * @param relationship
+ * the relationship to use for joining the tables
+ */
+ public FromItem(JoinType join, Relationship relationship) {
+ _join = join;
+ _leftSide = new FromItem(relationship.getPrimaryTable());
+ Column[] columns = relationship.getPrimaryColumns();
+ _leftOn = new SelectItem[columns.length];
+ for (int i = 0; i < columns.length; i++) {
+ _leftOn[i] = new SelectItem(columns[i]);
+ }
+ _rightSide = new FromItem(relationship.getForeignTable());
+ columns = relationship.getForeignColumns();
+ _rightOn = new SelectItem[columns.length];
+ for (int i = 0; i < columns.length; i++) {
+ _rightOn[i] = new SelectItem(columns[i]);
+ }
+ }
+
+ /**
+ * Constructor for advanced join types with custom relationships
+ *
+ * @param join
+ * the join type to use
+ * @param leftSide
+ * the left side of the join
+ * @param rightSide
+ * the right side of the join
+ * @param leftOn
+ * what left-side select items to use for the ON clause
+ * @param rightOn
+ * what right-side select items to use for the ON clause
+ */
+ public FromItem(JoinType join, FromItem leftSide, FromItem rightSide, SelectItem[] leftOn, SelectItem[] rightOn) {
+ _join = join;
+ _leftSide = leftSide;
+ _rightSide = rightSide;
+ _leftOn = leftOn;
+ _rightOn = rightOn;
+ }
+
+ /**
+ * Creates a single unvalidated from item based on a expression. Expression
+ * based from items are typically NOT datastore-neutral but are available
+ * for special "hacking" needs.
+ *
+ * Expression based from items can only be used for JDBC based datastores
+ * since they are translated directly into SQL.
+ *
+ * @param expression
+ * An expression to use for the from item, for example "MYTABLE".
+ */
+ public FromItem(String expression) {
+ if (expression == null) {
+ throw new IllegalArgumentException("Expression cannot be null");
+ }
+ _expression = expression;
+ }
+
+ public String getAlias() {
+ return _alias;
+ }
+
+ public String getSameQueryAlias() {
+ if (_alias != null) {
+ return _alias;
+ }
+ if (_table != null) {
+ return _table.getQuotedName();
+ }
+ return null;
+ }
+
+ public FromItem setAlias(String alias) {
+ _alias = alias;
+ return this;
+ }
+
+ public Table getTable() {
+ return _table;
+ }
+
+ public Query getSubQuery() {
+ return _subQuery;
+ }
+
+ public JoinType getJoin() {
+ return _join;
+ }
+
+ public FromItem getLeftSide() {
+ return _leftSide;
+ }
+
+ public FromItem getRightSide() {
+ return _rightSide;
+ }
+
+ public SelectItem[] getLeftOn() {
+ return _leftOn;
+ }
+
+ public SelectItem[] getRightOn() {
+ return _rightOn;
+ }
+
+ public String getExpression() {
+ return _expression;
+ }
+
+ @Override
+ public String toSql() {
+ return toSql(false);
+ }
+
+ @Override
+ public String toSql(boolean includeSchemaInColumnPaths) {
+ final String stringNoAlias = toStringNoAlias(includeSchemaInColumnPaths);
+ final StringBuilder sb = new StringBuilder(stringNoAlias);
+ if (_join != null && _alias != null) {
+ sb.insert(0, '(');
+ sb.append(')');
+ }
+ if (_alias != null) {
+ sb.append(' ');
+ sb.append(_alias);
+ }
+ return sb.toString();
+ }
+
+ public String toStringNoAlias() {
+ return toStringNoAlias(false);
+ }
+
+ public String toStringNoAlias(boolean includeSchemaInColumnPaths) {
+ if (_expression != null) {
+ return _expression;
+ }
+ StringBuilder sb = new StringBuilder();
+ if (_table != null) {
+ if (_table.getSchema() != null && _table.getSchema().getName() != null) {
+ sb.append(_table.getSchema().getName());
+ sb.append('.');
+ }
+ sb.append(_table.getQuotedName());
+ } else if (_subQuery != null) {
+ sb.append('(');
+ sb.append(_subQuery.toSql(includeSchemaInColumnPaths));
+ sb.append(')');
+ } else if (_join != null) {
+ String leftSideAlias = _leftSide.getSameQueryAlias();
+ String rightSideAlias = _rightSide.getSameQueryAlias();
+ sb.append(_leftSide.toSql());
+ sb.append(' ');
+ sb.append(_join);
+ sb.append(" JOIN ");
+ sb.append(_rightSide.toSql());
+ for (int i = 0; i < _leftOn.length; i++) {
+ if (i == 0) {
+ sb.append(" ON ");
+ } else {
+ sb.append(" AND ");
+ }
+ SelectItem primary = _leftOn[i];
+ appendJoinOnItem(sb, leftSideAlias, primary);
+
+ sb.append(" = ");
+
+ SelectItem foreign = _rightOn[i];
+ appendJoinOnItem(sb, rightSideAlias, foreign);
+ }
+ }
+ return sb.toString();
+ }
+
+ private void appendJoinOnItem(StringBuilder sb, String sideAlias, SelectItem onItem) {
+ final FromItem fromItem = onItem.getFromItem();
+ if (fromItem != null && fromItem.getSubQuery() != null && fromItem.getAlias() != null) {
+ // there's a corner case scenario where an ON item references a
+ // subquery being joined. In that case the getSuperQueryAlias()
+ // method will include the subquery alias.
+ final String superQueryAlias = onItem.getSuperQueryAlias();
+ sb.append(superQueryAlias);
+ return;
+ }
+
+ if (sideAlias != null) {
+ sb.append(sideAlias);
+ sb.append('.');
+ }
+ final String superQueryAlias = onItem.getSuperQueryAlias();
+ sb.append(superQueryAlias);
+ }
+
+ /**
+ * Gets the alias of a table, if it is registered (and visible, ie. not part
+ * of a sub-query) in the FromItem
+ *
+ * @param table
+ * the table to get the alias for
+ * @return the alias or null if none is found
+ */
+ public String getAlias(Table table) {
+ String result = null;
+ if (table != null) {
+ // Search recursively through left and right side, unless they
+ // are sub-query FromItems
+ if (table.equals(_table)) {
+ result = _alias;
+ } else if (_join != null) {
+ result = _rightSide.getAlias(table);
+ if (result == null) {
+ result = _leftSide.getAlias(table);
+ }
+ }
+ }
+ return result;
+ }
+
+ public Query getQuery() {
+ return _query;
+ }
+
+ public QueryItem setQuery(Query query) {
+ _query = query;
+ return this;
+ }
+
+ @Override
+ protected FromItem clone() {
+ FromItem f = new FromItem();
+ f._alias = _alias;
+ f._join = _join;
+ f._table = _table;
+ f._expression = _expression;
+ if (_subQuery != null) {
+ f._subQuery = _subQuery.clone();
+ }
+ if (_leftOn != null && _leftSide != null && _rightOn != null && _rightSide != null) {
+ f._leftSide = _leftSide.clone();
+ f._leftOn = _leftOn.clone();
+ f._rightSide = _rightSide.clone();
+ f._rightOn = _rightOn.clone();
+ }
+ return f;
+ }
+
+ @Override
+ protected void decorateIdentity(List<Object> identifiers) {
+ identifiers.add(_table);
+ identifiers.add(_alias);
+ identifiers.add(_subQuery);
+ identifiers.add(_join);
+ identifiers.add(_leftSide);
+ identifiers.add(_rightSide);
+ identifiers.add(_leftOn);
+ identifiers.add(_rightOn);
+ identifiers.add(_expression);
+ }
+
+ @Override
+ public String toString() {
+ return toSql();
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/query/FunctionType.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/FunctionType.java b/core/src/main/java/org/apache/metamodel/query/FunctionType.java
new file mode 100644
index 0000000..c8d79c7
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/query/FunctionType.java
@@ -0,0 +1,122 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.eobjects.metamodel.query;
+
+import org.eobjects.metamodel.schema.Column;
+import org.eobjects.metamodel.schema.ColumnType;
+import org.eobjects.metamodel.util.AggregateBuilder;
+
+/**
+ * Represents an aggregate function to use in a SelectItem.
+ *
+ * @see SelectItem
+ */
+public enum FunctionType {
+
+ COUNT {
+ @Override
+ public AggregateBuilder<Long> build() {
+ return new CountAggregateBuilder();
+ }
+ },
+ AVG {
+ @Override
+ public AggregateBuilder<Double> build() {
+ return new AverageAggregateBuilder();
+ }
+ },
+ SUM {
+ @Override
+ public AggregateBuilder<Double> build() {
+ return new SumAggregateBuilder();
+ }
+ },
+ MAX {
+ @Override
+ public AggregateBuilder<Object> build() {
+ return new MaxAggregateBuilder();
+ }
+ },
+ MIN {
+ @Override
+ public AggregateBuilder<Object> build() {
+ return new MinAggregateBuilder();
+ }
+ };
+
+ public ColumnType getExpectedColumnType(ColumnType type) {
+ switch (this) {
+ case COUNT:
+ return ColumnType.BIGINT;
+ case AVG:
+ case SUM:
+ return ColumnType.DOUBLE;
+ case MAX:
+ case MIN:
+ return type;
+ default:
+ return type;
+ }
+ }
+
+ public SelectItem createSelectItem(Column column) {
+ return new SelectItem(this, column);
+ }
+
+ public SelectItem createSelectItem(String expression, String alias) {
+ return new SelectItem(this, expression, alias);
+ }
+
+ public Object evaluate(Iterable<?> values) {
+ AggregateBuilder<?> builder = build();
+ for (Object object : values) {
+ builder.add(object);
+ }
+ return builder.getAggregate();
+ }
+
+ /**
+ * Executes the function
+ *
+ * @param values
+ * the values to be evaluated. If a value is null it won't be
+ * evaluated
+ * @return the result of the function execution. The return type class is
+ * dependent on the FunctionType and the values provided. COUNT
+ * yields a Long, AVG and SUM yields Double values and MAX and MIN
+ * yields the type of the provided values.
+ */
+ public Object evaluate(Object... values) {
+ AggregateBuilder<?> builder = build();
+ for (Object object : values) {
+ builder.add(object);
+ }
+ return builder.getAggregate();
+ }
+
+ public abstract AggregateBuilder<?> build();
+
+ public static FunctionType get(String functionName) {
+ try {
+ return valueOf(functionName);
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/query/GroupByClause.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/GroupByClause.java b/core/src/main/java/org/apache/metamodel/query/GroupByClause.java
new file mode 100644
index 0000000..8e7e6bd
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/query/GroupByClause.java
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.eobjects.metamodel.query;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents the GROUP BY clause of a query that contains GroupByItem's.
+ *
+ * @see GroupByItem
+ */
+public class GroupByClause extends AbstractQueryClause<GroupByItem> {
+
+ private static final long serialVersionUID = -3824934110331202101L;
+
+ public GroupByClause(Query query) {
+ super(query, AbstractQueryClause.PREFIX_GROUP_BY,
+ AbstractQueryClause.DELIM_COMMA);
+ }
+
+ public List<SelectItem> getEvaluatedSelectItems() {
+ final List<SelectItem> result = new ArrayList<SelectItem>();
+ final List<GroupByItem> items = getItems();
+ for (GroupByItem item : items) {
+ result.add(item.getSelectItem());
+ }
+ return result;
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/query/GroupByItem.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/GroupByItem.java b/core/src/main/java/org/apache/metamodel/query/GroupByItem.java
new file mode 100644
index 0000000..cf94623
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/query/GroupByItem.java
@@ -0,0 +1,91 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.eobjects.metamodel.query;
+
+import java.util.List;
+
+import org.eobjects.metamodel.util.BaseObject;
+
+/**
+ * Represents a GROUP BY item. GroupByItems always use a select item (that may
+ * or not be a part of the query already) for grouping.
+ *
+ * @see GroupByClause
+ */
+public class GroupByItem extends BaseObject implements QueryItem, Cloneable {
+
+ private static final long serialVersionUID = 5218878395877852919L;
+ private final SelectItem _selectItem;
+ private Query _query;
+
+ /**
+ * Constructs a GROUP BY item based on a select item that should be grouped.
+ *
+ * @param selectItem
+ */
+ public GroupByItem(SelectItem selectItem) {
+ if (selectItem == null) {
+ throw new IllegalArgumentException("SelectItem cannot be null");
+ }
+ _selectItem = selectItem;
+ }
+
+ public SelectItem getSelectItem() {
+ return _selectItem;
+ }
+
+ @Override
+ public String toSql() {
+ return toSql(false);
+ }
+
+ @Override
+ public String toSql(boolean includeSchemaInColumnPaths) {
+ final String sameQueryAlias = _selectItem.getSameQueryAlias(includeSchemaInColumnPaths);
+ return sameQueryAlias;
+ }
+
+ @Override
+ public String toString() {
+ return toSql();
+ }
+
+ public Query getQuery() {
+ return _query;
+ }
+
+ public GroupByItem setQuery(Query query) {
+ _query = query;
+ if (_selectItem != null) {
+ _selectItem.setQuery(query);
+ }
+ return this;
+ }
+
+ @Override
+ protected GroupByItem clone() {
+ GroupByItem g = new GroupByItem(_selectItem.clone());
+ return g;
+ }
+
+ @Override
+ protected void decorateIdentity(List<Object> identifiers) {
+ identifiers.add(_selectItem);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/query/JoinType.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/JoinType.java b/core/src/main/java/org/apache/metamodel/query/JoinType.java
new file mode 100644
index 0000000..c40ab0e
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/query/JoinType.java
@@ -0,0 +1,29 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.eobjects.metamodel.query;
+
+/**
+ * Represents a join type, used in a FromItem.
+ *
+ * @see FromItem
+ */
+public enum JoinType {
+
+ INNER, LEFT, RIGHT;
+}
http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/query/LogicalOperator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/LogicalOperator.java b/core/src/main/java/org/apache/metamodel/query/LogicalOperator.java
new file mode 100644
index 0000000..a17cb1e
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/query/LogicalOperator.java
@@ -0,0 +1,28 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.eobjects.metamodel.query;
+
+/**
+ * Represents a logical operator (AND or OR) to use when defining compound
+ * {@link FilterItem}s.
+ */
+public enum LogicalOperator {
+
+ AND, OR
+}
http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/query/MaxAggregateBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/MaxAggregateBuilder.java b/core/src/main/java/org/apache/metamodel/query/MaxAggregateBuilder.java
new file mode 100644
index 0000000..93591f1
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/query/MaxAggregateBuilder.java
@@ -0,0 +1,48 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.eobjects.metamodel.query;
+
+import org.eobjects.metamodel.util.AggregateBuilder;
+import org.eobjects.metamodel.util.ObjectComparator;
+
+final class MaxAggregateBuilder implements AggregateBuilder<Object> {
+
+ private Object max;
+
+ @Override
+ public void add(Object o) {
+ if (o == null) {
+ return;
+ }
+ if (max == null) {
+ max = o;
+ } else {
+ Comparable<Object> comparable = ObjectComparator.getComparable(max);
+ if (comparable.compareTo(o) < 0) {
+ max = o;
+ }
+ }
+ }
+
+ @Override
+ public Object getAggregate() {
+ return max;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/query/MinAggregateBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/MinAggregateBuilder.java b/core/src/main/java/org/apache/metamodel/query/MinAggregateBuilder.java
new file mode 100644
index 0000000..5321b22
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/query/MinAggregateBuilder.java
@@ -0,0 +1,48 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.eobjects.metamodel.query;
+
+import org.eobjects.metamodel.util.AggregateBuilder;
+import org.eobjects.metamodel.util.ObjectComparator;
+
+final class MinAggregateBuilder implements AggregateBuilder<Object> {
+
+ private Object min;
+
+ @Override
+ public void add(Object o) {
+ if (o == null) {
+ return;
+ }
+ if (min == null) {
+ min = o;
+ } else {
+ Comparable<Object> comparable = ObjectComparator.getComparable(min);
+ if (comparable.compareTo(o) > 0) {
+ min = o;
+ }
+ }
+ }
+
+ @Override
+ public Object getAggregate() {
+ return min;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/query/OperatorType.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/OperatorType.java b/core/src/main/java/org/apache/metamodel/query/OperatorType.java
new file mode 100644
index 0000000..3f4782e
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/query/OperatorType.java
@@ -0,0 +1,69 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.eobjects.metamodel.query;
+
+/**
+ * Defines the types of operators that can be used in filters.
+ *
+ * @see FilterItem
+ */
+public enum OperatorType {
+
+ EQUALS_TO("="), DIFFERENT_FROM("<>"), LIKE("LIKE"), GREATER_THAN(">"), LESS_THAN("<"), IN("IN"),
+
+ /**
+ * @deprecated use {@link #LESS_THAN} instead.
+ */
+ @Deprecated
+ LOWER_THAN("<"),
+
+ /**
+ * @deprecated use {@link #GREATER_THAN} instead.
+ */
+ @Deprecated
+ HIGHER_THAN(">");
+
+ private final String _sql;
+
+ private OperatorType(String sql) {
+ _sql = sql;
+ }
+
+ public String toSql() {
+ return _sql;
+ }
+
+ /**
+ * Converts from SQL string literals to an OperatorType. Valid SQL values
+ * are "=", "<>", "LIKE", ">" and "<".
+ *
+ * @param sqlType
+ * @return a OperatorType object representing the specified SQL type
+ */
+ public static OperatorType convertOperatorType(String sqlType) {
+ if (sqlType != null) {
+ for (OperatorType operator : values()) {
+ if (sqlType.equals(operator.toSql())) {
+ return operator;
+ }
+ }
+ }
+ return null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/query/OrderByClause.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/OrderByClause.java b/core/src/main/java/org/apache/metamodel/query/OrderByClause.java
new file mode 100644
index 0000000..8ce04ec
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/query/OrderByClause.java
@@ -0,0 +1,49 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.eobjects.metamodel.query;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents the ORDER BY clause of a query containing OrderByItem's. The order
+ * and direction of the OrderItems define the way that the result of a query
+ * will be sorted.
+ *
+ * @see OrderByItem
+ */
+public class OrderByClause extends AbstractQueryClause<OrderByItem> {
+
+ private static final long serialVersionUID = 2441926135870143715L;
+
+ public OrderByClause(Query query) {
+ super(query, AbstractQueryClause.PREFIX_ORDER_BY,
+ AbstractQueryClause.DELIM_COMMA);
+ }
+
+ public List<SelectItem> getEvaluatedSelectItems() {
+ final List<SelectItem> result = new ArrayList<SelectItem>();
+ final List<OrderByItem> items = getItems();
+ for (OrderByItem item : items) {
+ result.add(item.getSelectItem());
+ }
+ return result;
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/query/OrderByItem.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/OrderByItem.java b/core/src/main/java/org/apache/metamodel/query/OrderByItem.java
new file mode 100644
index 0000000..a52aa1d
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/query/OrderByItem.java
@@ -0,0 +1,152 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.eobjects.metamodel.query;
+
+import java.util.List;
+
+import org.eobjects.metamodel.util.BaseObject;
+
+/**
+ * Represents an ORDER BY item. An OrderByItem sorts the resulting DataSet
+ * according to a SelectItem that may or may not be a part of the query already.
+ *
+ * @see OrderByClause
+ * @see SelectItem
+ */
+public class OrderByItem extends BaseObject implements QueryItem, Cloneable {
+
+ public enum Direction {
+ ASC, DESC
+ }
+
+ private static final long serialVersionUID = -8397473619828484774L;
+ private final SelectItem _selectItem;
+ private Direction _direction;
+ private Query _query;
+
+ /**
+ * Creates an OrderByItem
+ *
+ * @param selectItem
+ * the select item to order
+ * @param direction
+ * the direction to order the select item
+ */
+ public OrderByItem(SelectItem selectItem, Direction direction) {
+ if (selectItem == null) {
+ throw new IllegalArgumentException("SelectItem cannot be null");
+ }
+ _selectItem = selectItem;
+ _direction = direction;
+ }
+
+ /**
+ * Creates an OrderByItem
+ *
+ * @param selectItem
+ * @param ascending
+ * @deprecated user OrderByItem(SelectItem, Direction) instead
+ */
+ @Deprecated
+ public OrderByItem(SelectItem selectItem, boolean ascending) {
+ if (selectItem == null) {
+ throw new IllegalArgumentException("SelectItem cannot be null");
+ }
+ _selectItem = selectItem;
+ if (ascending) {
+ _direction = Direction.ASC;
+ } else {
+ _direction = Direction.DESC;
+ }
+ }
+
+ /**
+ * Creates an ascending OrderByItem
+ *
+ * @param selectItem
+ */
+ public OrderByItem(SelectItem selectItem) {
+ this(selectItem, Direction.ASC);
+ }
+
+
+ @Override
+ public String toSql(boolean includeSchemaInColumnPaths) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(_selectItem.getSameQueryAlias(includeSchemaInColumnPaths) + ' ');
+ sb.append(_direction);
+ return sb.toString();
+ }
+
+ @Override
+ public String toSql() {
+ return toSql(false);
+ }
+
+ public boolean isAscending() {
+ return (_direction == Direction.ASC);
+ }
+
+ public boolean isDescending() {
+ return (_direction == Direction.DESC);
+ }
+
+ public Direction getDirection() {
+ return _direction;
+ }
+
+ public OrderByItem setDirection(Direction direction) {
+ _direction = direction;
+ return this;
+ }
+
+ public SelectItem getSelectItem() {
+ return _selectItem;
+ }
+
+ public Query getQuery() {
+ return _query;
+ }
+
+ public OrderByItem setQuery(Query query) {
+ _query = query;
+ if (_selectItem != null) {
+ _selectItem.setQuery(query);
+ }
+ return this;
+ }
+
+ @Override
+ protected OrderByItem clone() {
+ OrderByItem o = new OrderByItem(_selectItem.clone());
+ o._direction = _direction;
+ return o;
+ }
+
+ @Override
+ protected void decorateIdentity(List<Object> identifiers) {
+ identifiers.add(_direction);
+ identifiers.add(_selectItem);
+ }
+
+ @Override
+ public String toString() {
+ return toSql();
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/query/Query.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/Query.java b/core/src/main/java/org/apache/metamodel/query/Query.java
new file mode 100644
index 0000000..0986b6b
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/query/Query.java
@@ -0,0 +1,603 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.eobjects.metamodel.query;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eobjects.metamodel.DataContext;
+import org.eobjects.metamodel.MetaModelException;
+import org.eobjects.metamodel.query.OrderByItem.Direction;
+import org.eobjects.metamodel.query.parser.QueryParserException;
+import org.eobjects.metamodel.query.parser.QueryPartCollectionProcessor;
+import org.eobjects.metamodel.query.parser.QueryPartParser;
+import org.eobjects.metamodel.query.parser.QueryPartProcessor;
+import org.eobjects.metamodel.query.parser.SelectItemParser;
+import org.eobjects.metamodel.schema.Column;
+import org.eobjects.metamodel.schema.ColumnType;
+import org.eobjects.metamodel.schema.Table;
+import org.eobjects.metamodel.util.BaseObject;
+import org.eobjects.metamodel.util.BooleanComparator;
+import org.eobjects.metamodel.util.FormatHelper;
+import org.eobjects.metamodel.util.NumberComparator;
+
+/**
+ * Represents a query to retrieve data by. A query is made up of six clauses,
+ * equivalent to the SQL standard:
+ * <ul>
+ * <li>the SELECT clause, which define the wanted columns of the resulting
+ * DataSet</li>
+ * <li>the FROM clause, which define where to retrieve the data from</li>
+ * <li>the WHERE clause, which define filters on the retrieved data</li>
+ * <li>the GROUP BY clause, which define if the result should be grouped and
+ * aggregated according to some columns acting as categories</li>
+ * <li>the HAVING clause, which define filters on the grouped data</li>
+ * <li>the ORDER BY clause, which define sorting of the resulting dataset</li>
+ * </ul>
+ *
+ * In addition two properties are applied to queries to limit the resulting
+ * dataset:
+ * <ul>
+ * <li>First row: The first row (aka. offset) of the result of the query.</li>
+ * <li>Max rows: The maximum amount of rows to return when executing the query.</li>
+ * </ul>
+ *
+ * Queries are executed using the DataContext.executeQuery method or can
+ * alternatively be used directly in JDBC by using the toString() method.
+ *
+ * @see DataContext
+ */
+public final class Query extends BaseObject implements Cloneable, Serializable {
+
+ private static final long serialVersionUID = -5976325207498574216L;
+
+ private final SelectClause _selectClause;
+ private final FromClause _fromClause;
+ private final FilterClause _whereClause;
+ private final GroupByClause _groupByClause;
+ private final FilterClause _havingClause;
+ private final OrderByClause _orderByClause;
+
+ private Integer _maxRows;
+ private Integer _firstRow;
+
+ public Query() {
+ _selectClause = new SelectClause(this);
+ _fromClause = new FromClause(this);
+ _whereClause = new FilterClause(this, AbstractQueryClause.PREFIX_WHERE);
+ _groupByClause = new GroupByClause(this);
+ _havingClause = new FilterClause(this, AbstractQueryClause.PREFIX_HAVING);
+ _orderByClause = new OrderByClause(this);
+ }
+
+ public Query select(Column column, FromItem fromItem) {
+ SelectItem selectItem = new SelectItem(column, fromItem);
+ return select(selectItem);
+ }
+
+ public Query select(Column... columns) {
+ for (Column column : columns) {
+ SelectItem selectItem = new SelectItem(column);
+ selectItem.setQuery(this);
+ _selectClause.addItem(selectItem);
+ }
+ return this;
+ }
+
+ public Query select(SelectItem... items) {
+ _selectClause.addItems(items);
+ return this;
+ }
+
+ public Query select(FunctionType functionType, Column column) {
+ _selectClause.addItem(new SelectItem(functionType, column));
+ return this;
+ }
+
+ public Query select(String expression, String alias) {
+ return select(new SelectItem(expression, alias));
+ }
+
+ /**
+ * Adds a selection to this query.
+ *
+ * @param expression
+ * @return
+ */
+ public Query select(String expression) {
+ if ("*".equals(expression)) {
+ return selectAll();
+ }
+
+ SelectItem selectItem = findSelectItem(expression, true);
+ return select(selectItem);
+ }
+
+ private SelectItem findSelectItem(String expression, boolean allowExpressionBasedSelectItem) {
+ SelectItemParser parser = new SelectItemParser(this, allowExpressionBasedSelectItem);
+ return parser.findSelectItem(expression);
+ }
+
+ /**
+ * Select all available select items from all currently available FROM
+ * items. Equivalent of the expression "SELECT * FROM ..." in SQL.
+ *
+ * @return
+ */
+ public Query selectAll() {
+ List<FromItem> items = getFromClause().getItems();
+ for (FromItem fromItem : items) {
+ selectAll(fromItem);
+ }
+ return this;
+ }
+
+ public Query selectAll(final FromItem fromItem) {
+ if (fromItem.getTable() != null) {
+ final Column[] columns = fromItem.getTable().getColumns();
+ for (final Column column : columns) {
+ select(column, fromItem);
+ }
+ } else if (fromItem.getJoin() != null) {
+ selectAll(fromItem.getLeftSide());
+ selectAll(fromItem.getRightSide());
+ } else if (fromItem.getSubQuery() != null) {
+ final List<SelectItem> items = fromItem.getSubQuery().getSelectClause().getItems();
+ for (final SelectItem subQuerySelectItem : items) {
+ select(new SelectItem(subQuerySelectItem, fromItem));
+ }
+ } else {
+ throw new MetaModelException("All select items ('*') not determinable with from item: " + fromItem);
+ }
+ return this;
+ }
+
+ public Query selectDistinct() {
+ _selectClause.setDistinct(true);
+ return this;
+ }
+
+ public Query selectCount() {
+ return select(SelectItem.getCountAllItem());
+ }
+
+ public Query from(FromItem... items) {
+ _fromClause.addItems(items);
+ return this;
+ }
+
+ public Query from(Table table) {
+ return from(new FromItem(table));
+ }
+
+ public Query from(String expression) {
+ return from(new FromItem(expression));
+ }
+
+ public Query from(Table table, String alias) {
+ return from(new FromItem(table).setAlias(alias));
+ }
+
+ public Query from(Table leftTable, Table rightTable, JoinType joinType, Column leftOnColumn, Column rightOnColumn) {
+ SelectItem[] leftOn = new SelectItem[] { new SelectItem(leftOnColumn) };
+ SelectItem[] rightOn = new SelectItem[] { new SelectItem(rightOnColumn) };
+ FromItem fromItem = new FromItem(joinType, new FromItem(leftTable), new FromItem(rightTable), leftOn, rightOn);
+ return from(fromItem);
+ }
+
+ public Query groupBy(String... groupByTokens) {
+ for (String groupByToken : groupByTokens) {
+ SelectItem selectItem = findSelectItem(groupByToken, true);
+ groupBy(new GroupByItem(selectItem));
+ }
+ return this;
+ }
+
+ public Query groupBy(GroupByItem... items) {
+ for (GroupByItem item : items) {
+ SelectItem selectItem = item.getSelectItem();
+ if (selectItem != null && selectItem.getQuery() == null) {
+ selectItem.setQuery(this);
+ }
+ }
+ _groupByClause.addItems(items);
+ return this;
+ }
+
+ public Query groupBy(Column... columns) {
+ for (Column column : columns) {
+ SelectItem selectItem = new SelectItem(column).setQuery(this);
+ _groupByClause.addItem(new GroupByItem(selectItem));
+ }
+ return this;
+ }
+
+ public Query orderBy(OrderByItem... items) {
+ _orderByClause.addItems(items);
+ return this;
+ }
+
+ public Query orderBy(String... orderByTokens) {
+ for (String orderByToken : orderByTokens) {
+ orderByToken = orderByToken.trim();
+ final Direction direction;
+ if (orderByToken.toUpperCase().endsWith("DESC")) {
+ direction = Direction.DESC;
+ orderByToken = orderByToken.substring(0, orderByToken.length() - 4).trim();
+ } else if (orderByToken.toUpperCase().endsWith("ASC")) {
+ direction = Direction.ASC;
+ orderByToken = orderByToken.substring(0, orderByToken.length() - 3).trim();
+ } else {
+ direction = Direction.ASC;
+ }
+
+ OrderByItem orderByItem = new OrderByItem(findSelectItem(orderByToken, true), direction);
+ orderBy(orderByItem);
+ }
+ return this;
+ }
+
+ public Query orderBy(Column column) {
+ return orderBy(column, Direction.ASC);
+ }
+
+ /**
+ * @deprecated use orderBy(Column, Direction) instead
+ */
+ @Deprecated
+ public Query orderBy(Column column, boolean ascending) {
+ if (ascending) {
+ return orderBy(column, Direction.ASC);
+ } else {
+ return orderBy(column, Direction.DESC);
+ }
+ }
+
+ public Query orderBy(Column column, Direction direction) {
+ SelectItem selectItem = _selectClause.getSelectItem(column);
+ if (selectItem == null) {
+ selectItem = new SelectItem(column);
+ }
+ return orderBy(new OrderByItem(selectItem, direction));
+ }
+
+ public Query where(FilterItem... items) {
+ _whereClause.addItems(items);
+ return this;
+ }
+
+ public Query where(Iterable<FilterItem> items) {
+ _whereClause.addItems(items);
+ return this;
+ }
+
+ public Query where(String... whereItemTokens) {
+ for (String whereItemToken : whereItemTokens) {
+ FilterItem filterItem = findFilterItem(whereItemToken);
+ where(filterItem);
+ }
+ return this;
+ }
+
+ private FilterItem findFilterItem(String expression) {
+ final QueryPartCollectionProcessor collectionProcessor = new QueryPartCollectionProcessor();
+ new QueryPartParser(collectionProcessor, expression, " AND ", " OR ").parse();
+
+ final List<String> tokens = collectionProcessor.getTokens();
+ final List<String> delims = collectionProcessor.getDelims();
+ if (tokens.size() == 1) {
+ expression = tokens.get(0);
+ } else {
+ final LogicalOperator logicalOperator = LogicalOperator.valueOf(delims.get(1).trim());
+
+ final List<FilterItem> filterItems = new ArrayList<FilterItem>();
+ for (int i = 0; i < tokens.size(); i++) {
+ String token = tokens.get(i);
+ FilterItem filterItem = findFilterItem(token);
+ filterItems.add(filterItem);
+ }
+ return new FilterItem(logicalOperator, filterItems);
+ }
+
+ OperatorType operator = null;
+ String leftSide = null;
+ final String rightSide;
+ {
+ String rightSideCandidate = null;
+ final OperatorType[] operators = OperatorType.values();
+ for (OperatorType operatorCandidate : operators) {
+ final int operatorIndex = expression.indexOf(' ' + operatorCandidate.toSql() + ' ');
+ if (operatorIndex > 0) {
+ operator = operatorCandidate;
+ leftSide = expression.substring(0, operatorIndex).trim();
+ rightSideCandidate = expression.substring(operatorIndex + operator.toSql().length() + 2).trim();
+ break;
+ }
+ }
+
+ if (operator == null) {
+ // check special cases for IS NULL and IS NOT NULL
+ if (expression.endsWith(" IS NOT NULL")) {
+ operator = OperatorType.DIFFERENT_FROM;
+ leftSide = expression.substring(0, expression.lastIndexOf(" IS NOT NULL")).trim();
+ rightSideCandidate = "NULL";
+ } else if (expression.endsWith(" IS NULL")) {
+ operator = OperatorType.EQUALS_TO;
+ leftSide = expression.substring(0, expression.lastIndexOf(" IS NULL")).trim();
+ rightSideCandidate = "NULL";
+ }
+ }
+
+ rightSide = rightSideCandidate;
+ }
+
+ if (operator == null) {
+ return new FilterItem(expression);
+ }
+
+ final SelectItem selectItem = findSelectItem(leftSide, false);
+ if (selectItem == null) {
+ return new FilterItem(expression);
+ }
+
+ final Object operand;
+ if (operator == OperatorType.IN) {
+ final List<Object> list = new ArrayList<Object>();
+ new QueryPartParser(new QueryPartProcessor() {
+ @Override
+ public void parse(String delim, String itemToken) {
+ Object operand = createOperand(itemToken, selectItem, false);
+ list.add(operand);
+ }
+ }, rightSide, ",").parse();
+ operand = list;
+ } else {
+ operand = createOperand(rightSide, selectItem, true);
+ }
+
+ return new FilterItem(selectItem, operator, operand);
+ }
+
+ private Object createOperand(final String token, final SelectItem leftSelectItem, final boolean searchSelectItems) {
+ if (token.equalsIgnoreCase("NULL")) {
+ return null;
+ }
+
+ if (token.startsWith("'") && token.endsWith("'") && token.length() > 2) {
+ String stringOperand = token.substring(1, token.length() - 1);
+ stringOperand = stringOperand.replaceAll("\\\\'", "'");
+ return stringOperand;
+ }
+
+ if (searchSelectItems) {
+ final SelectItem selectItem = findSelectItem(token, false);
+ if (selectItem != null) {
+ return selectItem;
+ }
+ }
+
+ final ColumnType expectedColumnType = leftSelectItem.getExpectedColumnType();
+ final Object result;
+ if (expectedColumnType == null) {
+ // We're assuming number here, but it could also be boolean or a
+ // time based type. But anyways, this should not happen since
+ // expected column type should be available.
+ result = NumberComparator.toNumber(token);
+ } else if (expectedColumnType.isBoolean()) {
+ result = BooleanComparator.toBoolean(token);
+ } else if (expectedColumnType.isTimeBased()) {
+ result = FormatHelper.parseSqlTime(expectedColumnType, token);
+ } else {
+ result = NumberComparator.toNumber(token);
+ }
+
+ if (result == null) {
+ // shouldn't happen since only "NULL" is parsed as null.
+ throw new QueryParserException("Could not parse operand: " + token);
+ }
+
+ return result;
+ }
+
+ public Query where(SelectItem selectItem, OperatorType operatorType, Object operand) {
+ return where(new FilterItem(selectItem, operatorType, operand));
+ }
+
+ public Query where(Column column, OperatorType operatorType, Object operand) {
+ SelectItem selectItem = _selectClause.getSelectItem(column);
+ if (selectItem == null) {
+ selectItem = new SelectItem(column);
+ }
+ return where(selectItem, operatorType, operand);
+ }
+
+ public Query having(FilterItem... items) {
+ _havingClause.addItems(items);
+ return this;
+ }
+
+ public Query having(FunctionType function, Column column, OperatorType operatorType, Object operand) {
+ SelectItem selectItem = new SelectItem(function, column);
+ return having(new FilterItem(selectItem, operatorType, operand));
+ }
+
+ public Query having(Column column, OperatorType operatorType, Object operand) {
+ SelectItem selectItem = _selectClause.getSelectItem(column);
+ if (selectItem == null) {
+ selectItem = new SelectItem(column);
+ }
+ return having(new FilterItem(selectItem, operatorType, operand));
+ }
+
+ public Query having(String... havingItemTokens) {
+ for (String havingItemToken : havingItemTokens) {
+ FilterItem filterItem = findFilterItem(havingItemToken);
+ having(filterItem);
+ }
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return toSql();
+ }
+
+ /*
+ * A string representation of this query. This representation will be SQL 99
+ * compatible and can thus be used for database queries on databases that
+ * meet SQL standards.
+ */
+ public String toSql() {
+ return toSql(false);
+ }
+
+ protected String toSql(boolean includeSchemaInColumnPaths) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append(_selectClause.toSql(includeSchemaInColumnPaths));
+ sb.append(_fromClause.toSql(includeSchemaInColumnPaths));
+ sb.append(_whereClause.toSql(includeSchemaInColumnPaths));
+ sb.append(_groupByClause.toSql(includeSchemaInColumnPaths));
+ sb.append(_havingClause.toSql(includeSchemaInColumnPaths));
+ sb.append(_orderByClause.toSql(includeSchemaInColumnPaths));
+ return sb.toString();
+ }
+
+ public SelectClause getSelectClause() {
+ return _selectClause;
+ }
+
+ public FromClause getFromClause() {
+ return _fromClause;
+ }
+
+ public FilterClause getWhereClause() {
+ return _whereClause;
+ }
+
+ public GroupByClause getGroupByClause() {
+ return _groupByClause;
+ }
+
+ public FilterClause getHavingClause() {
+ return _havingClause;
+ }
+
+ public OrderByClause getOrderByClause() {
+ return _orderByClause;
+ }
+
+ /**
+ * Sets the maximum number of rows to be queried. If the result of the query
+ * yields more rows they should be discarded.
+ *
+ * @param maxRows
+ * the number of desired maximum rows. Can be null (default) for
+ * no limits
+ * @return this query
+ */
+ public Query setMaxRows(Integer maxRows) {
+ if (maxRows != null) {
+ final int maxRowsValue = maxRows.intValue();
+ if (maxRowsValue == 0) {
+ throw new IllegalArgumentException("Max rows cannot be zero");
+ }
+ if (maxRowsValue < 0) {
+ throw new IllegalArgumentException("Max rows cannot be negative");
+ }
+ }
+ _maxRows = maxRows;
+ return this;
+ }
+
+ /**
+ * @return the number of maximum rows to yield from executing this query or
+ * null if no maximum/limit is set.
+ */
+ public Integer getMaxRows() {
+ return _maxRows;
+ }
+
+ /**
+ * Sets the first row (aka offset) of the query's result. The row number is
+ * 1-based, so setting a first row value of 1 is equivalent to not setting
+ * it at all..
+ *
+ * @param firstRow
+ * the first row, where 1 is the first row.
+ * @return this query
+ */
+ public Query setFirstRow(Integer firstRow) {
+ if (firstRow != null && firstRow.intValue() < 1) {
+ throw new IllegalArgumentException("First row cannot be negative or zero");
+ }
+ _firstRow = firstRow;
+ return this;
+ }
+
+ /**
+ * Gets the first row (aka offset) of the query's result, or null if none is
+ * specified. The row number is 1-based, so setting a first row value of 1
+ * is equivalent to not setting it at all..
+ *
+ * @return the first row (aka offset) of the query's result, or null if no
+ * offset is specified.
+ */
+ public Integer getFirstRow() {
+ return _firstRow;
+ }
+
+ @Override
+ protected void decorateIdentity(List<Object> identifiers) {
+ identifiers.add(_maxRows);
+ identifiers.add(_selectClause);
+ identifiers.add(_fromClause);
+ identifiers.add(_whereClause);
+ identifiers.add(_groupByClause);
+ identifiers.add(_havingClause);
+ identifiers.add(_orderByClause);
+ }
+
+ @Override
+ public Query clone() {
+ Query q = new Query();
+ q.setMaxRows(_maxRows);
+ q.setFirstRow(_firstRow);
+ q.getSelectClause().setDistinct(_selectClause.isDistinct());
+ for (FromItem item : _fromClause.getItems()) {
+ q.from(item.clone());
+ }
+ for (SelectItem item : _selectClause.getItems()) {
+ q.select(item.clone());
+ }
+ for (FilterItem item : _whereClause.getItems()) {
+ q.where(item.clone());
+ }
+ for (GroupByItem item : _groupByClause.getItems()) {
+ q.groupBy(item.clone());
+ }
+ for (FilterItem item : _havingClause.getItems()) {
+ q.having(item.clone());
+ }
+ for (OrderByItem item : _orderByClause.getItems()) {
+ q.orderBy(item.clone());
+ }
+ return q;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/query/QueryClause.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/QueryClause.java b/core/src/main/java/org/apache/metamodel/query/QueryClause.java
new file mode 100644
index 0000000..e3dddc8
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/query/QueryClause.java
@@ -0,0 +1,53 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.eobjects.metamodel.query;
+
+import java.io.Serializable;
+import java.util.List;
+
+public interface QueryClause<E> extends Serializable {
+
+ public QueryClause<E> setItems(E... items);
+
+ public QueryClause<E> addItems(E... items);
+
+ public QueryClause<E> addItems(Iterable<E> items);
+
+ public QueryClause<E> addItem(int index, E item);
+
+ public QueryClause<E> addItem(E item);
+
+ public boolean isEmpty();
+
+ public int getItemCount();
+
+ public E getItem(int index);
+
+ public List<E> getItems();
+
+ public QueryClause<E> removeItem(int index);
+
+ public QueryClause<E> removeItem(E item);
+
+ public QueryClause<E> removeItems();
+
+ public String toSql(boolean includeSchemaInColumnPaths);
+
+ public String toSql();
+}
http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/query/QueryItem.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/QueryItem.java b/core/src/main/java/org/apache/metamodel/query/QueryItem.java
new file mode 100644
index 0000000..ab79959
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/query/QueryItem.java
@@ -0,0 +1,39 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.eobjects.metamodel.query;
+
+import java.io.Serializable;
+
+/**
+ * Interface for items in a query. All QueryItems reside within a QueryClause.
+ *
+ * @see AbstractQueryClause
+ */
+public interface QueryItem extends Serializable {
+
+ public QueryItem setQuery(Query query);
+
+ public Query getQuery();
+
+ public String toSql();
+
+ public String toSql(boolean includeSchemaInColumnPaths);
+
+ public String toString();
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/e2e2b37a/core/src/main/java/org/apache/metamodel/query/QueryParameter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/QueryParameter.java b/core/src/main/java/org/apache/metamodel/query/QueryParameter.java
new file mode 100644
index 0000000..c8c0401
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/query/QueryParameter.java
@@ -0,0 +1,37 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.eobjects.metamodel.query;
+
+import org.eobjects.metamodel.DataContext;
+
+/**
+ * Represents a query parameter, in SQL represented with a '?' symbol.
+ * Parameters are values in the query that will be defined at execution time,
+ * not parsing/preparation time.
+ *
+ * @see CompiledQuery
+ * @see DataContext#compileQuery(Query)
+ */
+public class QueryParameter {
+
+ @Override
+ public String toString() {
+ return "?";
+ }
+}