001: /**********************************************************************
002: Copyright (c) 2004 Erik Bengtson and others. All rights reserved.
003: Licensed under the Apache License, Version 2.0 (the "License");
004: you may not use this file except in compliance with the License.
005: You may obtain a copy of the License at
006:
007: http://www.apache.org/licenses/LICENSE-2.0
008:
009: Unless required by applicable law or agreed to in writing, software
010: distributed under the License is distributed on an "AS IS" BASIS,
011: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: See the License for the specific language governing permissions and
013: limitations under the License.
014:
015:
016: Contributors:
017: 2005 Andy Jefferson - fix for count(this) where we have a composite PK
018: ...
019: **********************************************************************/package org.jpox.store.expression;
020:
021: import java.util.ArrayList;
022: import java.util.List;
023: import java.util.ListIterator;
024:
025: import org.jpox.exceptions.JPOXUserException;
026: import org.jpox.store.mapping.IntegerMapping;
027: import org.jpox.store.mapping.JavaTypeMapping;
028: import org.jpox.store.mapping.ShortMapping;
029:
030: /**
031: * Representation of aggregate functions in JDOQL
032: * @version $Revision: 1.16 $
033: */
034: public class AggregateExpression extends NumericExpression {
035: /** Whether we should use DISTINCT on the aggregate field column. */
036: boolean distinct = false;
037:
038: /**
039: * Constructor
040: * @param qs The query statement
041: */
042: public AggregateExpression(QueryExpression qs) {
043: super (qs);
044: }
045:
046: protected AggregateExpression(JavaTypeMapping mapping,
047: String functionName, List args, boolean distinct) {
048: super (functionName, args);
049:
050: // Remove ScalarExpression generated SQL since it cant handle what we want
051: st.clearStatement();
052: st.append(functionName).append('(');
053: if (distinct) {
054: st.append("DISTINCT ");
055: }
056:
057: ListIterator i = args.listIterator();
058: ScalarExpression arg = (ScalarExpression) i.next();
059: te = arg.getLogicSetExpression(); //just delegate table expr to one arg.
060: st.append(arg);
061:
062: while (i.hasNext()) {
063: arg = (ScalarExpression) i.next();
064: st.append(',').append(arg);
065: }
066:
067: st.append(')');
068: this .expressionList.addExpression(this );
069: this .mapping = mapping;
070: }
071:
072: /**
073: * Method to enable use of DISTINCT on the aggregate field column
074: */
075: public void setDistinct() {
076: distinct = true;
077: }
078:
079: /**
080: * Returns the max value of the argument.
081: * @param expr the expression
082: * @return the result in a ScalarExpression instance
083: */
084: public ScalarExpression maxMethod(ScalarExpression expr) {
085: if (distinct) {
086: throw new JPOXUserException(LOCALISER.msg("037012", "MAX"));
087: }
088: return getFunctionExpression("MAX", expr, expr.getMapping());
089: }
090:
091: /**
092: * Returns the max value of the argument.
093: * @param expr the expression
094: * @return the result in a ScalarExpression instance
095: */
096: public ScalarExpression minMethod(ScalarExpression expr) {
097: if (distinct) {
098: throw new JPOXUserException(LOCALISER.msg("037012", "MIN"));
099: }
100: return getFunctionExpression("MIN", expr, expr.getMapping());
101: }
102:
103: /**
104: * Returns the sum value of the argument.
105: * @param expr the expression
106: * @return the result in a ScalarExpression instance
107: */
108: public ScalarExpression sumMethod(ScalarExpression expr) {
109: if (!(expr instanceof NumericExpression)) {
110: // JDO2 spec [14.6.9] : SUM is invalid on non-numeric types
111: throw new JPOXUserException(LOCALISER.msg("037011", "SUM"));
112: }
113: JavaTypeMapping sumMapping = expr.getMapping();
114: if (sumMapping instanceof IntegerMapping
115: || sumMapping instanceof ShortMapping) {
116: // Integral types return Long
117: sumMapping = qs.getStoreManager().getDatastoreAdapter()
118: .getMapping(Long.class, qs.getStoreManager(),
119: qs.getClassLoaderResolver());
120: }
121: return getFunctionExpression("SUM", expr, sumMapping);
122: }
123:
124: /**
125: * Returns the average value of the argument.
126: * @param expr the expression
127: * @return the result in a ScalarExpression instance
128: */
129: public ScalarExpression avgMethod(ScalarExpression expr) {
130: return getFunctionExpression("AVG", expr, expr.getMapping());
131: }
132:
133: /**
134: * Returns the count value of the argument.
135: * @param expr the expression
136: * @return the result in a ScalarExpression instance
137: */
138: public ScalarExpression countMethod(ScalarExpression expr) {
139: if (expr instanceof ObjectExpression
140: && expr.getMapping().getNumberOfDatastoreFields() > 1) {
141: // Use first column only with COUNT - COUNT(a,b) is invalid for RDBMS
142: ((ObjectExpression) expr).useFirstDatastoreFieldOnly();
143: }
144: return getFunctionExpression("COUNT", expr, qs
145: .getStoreManager().getDatastoreAdapter().getMapping(
146: Long.class, qs.getStoreManager(),
147: qs.getClassLoaderResolver()));
148: }
149:
150: /**
151: * Creates a function expression. e.g. MAX(expr)
152: * @param functionName the name of the function
153: * @param expr the expression
154: * @param mapping the mapping to be used for converting from db to java and vice-versa
155: * @return the function expression. a NullLiteral is returned if the argument expr is null
156: */
157: private ScalarExpression getFunctionExpression(String functionName,
158: ScalarExpression expr, JavaTypeMapping mapping) {
159: if (expr == null) {
160: return new NullLiteral(qs);
161: } else {
162: ArrayList args = new ArrayList();
163: args.add(expr);
164:
165: ScalarExpression numExpr = new AggregateExpression(mapping,
166: functionName, args, distinct);
167: return numExpr;
168: }
169: }
170: }
|