View Javadoc

1   /*
2    * Copyright (c) 2010.  The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  
21  package org.apache.expreval.expr;
22  
23  import org.apache.expreval.client.InternalErrorException;
24  import org.apache.expreval.client.NullColumnValueException;
25  import org.apache.expreval.client.ResultMissingColumnException;
26  import org.apache.expreval.expr.literal.DoubleLiteral;
27  import org.apache.expreval.expr.literal.FloatLiteral;
28  import org.apache.expreval.expr.literal.IntegerLiteral;
29  import org.apache.expreval.expr.literal.LongLiteral;
30  import org.apache.expreval.expr.literal.ShortLiteral;
31  import org.apache.expreval.expr.node.BooleanValue;
32  import org.apache.expreval.expr.node.DateValue;
33  import org.apache.expreval.expr.node.GenericValue;
34  import org.apache.expreval.expr.node.NumberValue;
35  import org.apache.expreval.expr.node.ObjectValue;
36  import org.apache.expreval.expr.node.StringValue;
37  import org.apache.hadoop.hbase.client.Result;
38  import org.apache.hadoop.hbase.filter.CompareFilter;
39  import org.apache.hadoop.hbase.filter.Filter;
40  import org.apache.hadoop.hbase.filter.WritableByteArrayComparable;
41  import org.apache.hadoop.hbase.hbql.client.HBqlException;
42  import org.apache.hadoop.hbase.hbql.filter.SingleColumnValueFilter;
43  import org.apache.hadoop.hbase.hbql.impl.AggregateValue;
44  import org.apache.hadoop.hbase.hbql.impl.InvalidServerFilterException;
45  import org.apache.hadoop.hbase.hbql.impl.InvalidTypeException;
46  import org.apache.hadoop.hbase.hbql.mapping.ColumnAttrib;
47  import org.apache.hadoop.hbase.hbql.util.Lists;
48  
49  import java.util.Arrays;
50  import java.util.List;
51  import java.util.concurrent.atomic.AtomicBoolean;
52  
53  public abstract class GenericExpression implements GenericValue {
54  
55      // These are used to cache type of the args for exprs with numberic args
56      private Class<? extends GenericValue> highestRankingNumericArgFoundInValidate = NumberValue.class;
57      private Class rankingClass = null;
58      private boolean useDecimal = false;
59      private boolean useByte = false;
60      private boolean useShort = false;
61      private boolean useInteger = false;
62      private boolean useLong = false;
63      private boolean useFloat = false;
64      private boolean useDouble = false;
65  
66      private final ExpressionType type;
67      private final List<GenericValue> genericValueList = Lists.newArrayList();
68      private final AtomicBoolean allArgsOptimized = new AtomicBoolean(false);
69  
70      private MultipleExpressionContext expressionContext = null;
71  
72      private Class getRankingClass() {
73          return this.rankingClass;
74      }
75  
76      protected GenericExpression(final ExpressionType type, final GenericValue... exprs) {
77          this(type, Arrays.asList(exprs));
78      }
79  
80      protected GenericExpression(final ExpressionType type, final List<GenericValue> genericValueList) {
81          this.type = type;
82          if (genericValueList != null)
83              this.getGenericValueList().addAll(genericValueList);
84      }
85  
86      protected GenericExpression(final ExpressionType type,
87                                  final GenericValue expr,
88                                  final List<GenericValue> genericValueList) {
89          this.type = type;
90          this.getGenericValueList().add(expr);
91          if (genericValueList != null)
92              this.getGenericValueList().addAll(genericValueList);
93      }
94  
95      protected FunctionTypeSignature getTypeSignature() {
96          return this.type.getTypeSignature();
97      }
98  
99      public List<GenericValue> getGenericValueList() {
100         return this.genericValueList;
101     }
102 
103     protected List<GenericValue> getSubArgs(final int i) {
104         return this.getGenericValueList().subList(i, this.getGenericValueList().size());
105     }
106 
107     private Class<? extends GenericValue> getHighestRankingNumericArgFoundInValidate() {
108         return this.highestRankingNumericArgFoundInValidate;
109     }
110 
111     // These require getHighestRankingNumericArg() be called first to set value
112     protected boolean useDecimal() {
113         return this.useDecimal;
114     }
115 
116     protected Number getValueWithCast(final long result) throws HBqlException {
117         if (this.useByte)
118             return (byte)result;
119         else if (this.useShort)
120             return (short)result;
121         else if (this.useInteger)
122             return (int)result;
123         else if (this.useLong)
124             return result;
125         else
126             throw new HBqlException("Invalid class: " + this.getRankingClass().getName());
127     }
128 
129     protected Number getValueWithCast(final double result) throws HBqlException {
130         if (this.useFloat)
131             return (float)result;
132         else if (this.useDouble)
133             return result;
134         else
135             throw new HBqlException("Invalid class: " + this.getRankingClass().getName());
136     }
137 
138     protected Class validateNumericArgTypes(final Object... objs) {
139 
140         if (this.getRankingClass() == null) {
141 
142             // If we do not already know the specific types, then look at the class of both args
143             if (this.getHighestRankingNumericArgFoundInValidate() == NumberValue.class)
144                 this.rankingClass = NumericType.getHighestRankingNumericArg(objs);
145             else
146                 this.rankingClass = this.getHighestRankingNumericArgFoundInValidate();
147 
148             this.useDecimal = NumericType.useDecimalNumericArgs(this.getRankingClass());
149 
150             this.useByte = NumericType.isAByte(this.getRankingClass());
151             this.useShort = NumericType.isAShort(this.getRankingClass());
152             this.useInteger = NumericType.isAnInteger(this.getRankingClass());
153             this.useLong = NumericType.isALong(this.getRankingClass());
154             this.useFloat = NumericType.isAFloat(this.getRankingClass());
155             this.useDouble = NumericType.isADouble(this.getRankingClass());
156         }
157 
158         return this.getRankingClass();
159     }
160 
161     public boolean isAConstant() {
162 
163         if (this.getGenericValueList().size() == 0)
164             return false;
165 
166         for (final GenericValue val : this.getGenericValueList())
167             if (!val.isAConstant())
168                 return false;
169 
170         return true;
171     }
172 
173     public boolean isDefaultKeyword() {
174         return false;
175     }
176 
177     public boolean isAnAggregateValue() {
178         return false;
179     }
180 
181     public void initAggregateValue(final AggregateValue aggregateValue) throws HBqlException {
182         throw new InternalErrorException("Not applicable");
183     }
184 
185     public void applyResultToAggregateValue(final AggregateValue aggregateValue, final Result result) throws HBqlException, ResultMissingColumnException, NullColumnValueException {
186         throw new InternalErrorException("Not applicable");
187     }
188 
189     public boolean hasAColumnReference() {
190         for (final GenericValue val : this.getGenericValueList())
191             if (val.hasAColumnReference())
192                 return true;
193         return false;
194     }
195 
196     public boolean isAColumnReference() {
197         return false;
198     }
199 
200     public void reset() {
201         for (final GenericValue val : this.getGenericValueList())
202             val.reset();
203     }
204 
205     public void setExpressionContext(final MultipleExpressionContext expressionContext) throws HBqlException {
206 
207         this.expressionContext = expressionContext;
208 
209         for (final GenericValue val : this.getGenericValueList())
210             val.setExpressionContext(expressionContext);
211     }
212 
213     protected MultipleExpressionContext getExpressionContext() {
214         return this.expressionContext;
215     }
216 
217     private AtomicBoolean getAllArgsOptimized() {
218         return this.allArgsOptimized;
219     }
220 
221     protected void optimizeAllArgs() throws HBqlException {
222         if (!this.getAllArgsOptimized().get())
223             synchronized (this) {
224                 if (!this.getAllArgsOptimized().get()) {
225                     for (int i = 0; i < this.getGenericValueList().size(); i++)
226                         this.setArg(i, this.getExprArg(i).getOptimizedValue());
227                     this.getAllArgsOptimized().set(true);
228                 }
229             }
230     }
231 
232     protected Filter newSingleColumnValueFilter(final ColumnAttrib attrib,
233                                                 final CompareFilter.CompareOp compareOp,
234                                                 final WritableByteArrayComparable comparator) throws HBqlException {
235 
236         // Bail if expression uses a row key.
237         if (attrib.isAKeyAttrib())
238             throw new InvalidServerFilterException("Cannot use a key attribute");
239 
240         final SingleColumnValueFilter filter = new SingleColumnValueFilter(attrib.getFamilyNameAsBytes(),
241                                                                            attrib.getColumnNameAsBytes(),
242                                                                            compareOp,
243                                                                            comparator);
244         filter.setFilterIfMissing(true);
245         return filter;
246     }
247 
248     protected Object getConstantValue(final int pos) throws HBqlException {
249         try {
250             return this.getExprArg(pos).getValue(null, null);
251         }
252         catch (ResultMissingColumnException e) {
253             throw new InternalErrorException("Missing column: " + e.getMessage());
254         }
255         catch (NullColumnValueException e) {
256             throw new InternalErrorException("Null value: " + e.getMessage());
257         }
258     }
259 
260     public GenericValue getExprArg(final int i) {
261         return this.getGenericValueList().get(i);
262     }
263 
264     public void setArg(final int i, final GenericValue val) {
265         this.getGenericValueList().set(i, val);
266     }
267 
268     public Class<? extends GenericValue> validateTypes(final GenericValue parentExpr,
269                                                        final boolean allowCollections) throws HBqlException {
270 
271         if (this.getGenericValueList().size() != this.getTypeSignature().getArgCount())
272             throw new InvalidTypeException("Incorrect number of arguments in " + this.asString());
273 
274         final FunctionTypeSignature typeSignature = this.getTypeSignature();
275 
276         for (int i = 0; i < typeSignature.getArgCount(); i++)
277             this.validateParentClass(typeSignature.getArg(i), this.getExprArg(i).validateTypes(this, false));
278 
279         return typeSignature.getReturnType();
280     }
281 
282     protected Class<? extends GenericValue> validateNumericTypes() throws HBqlException {
283 
284         if (this.getGenericValueList().size() != this.getTypeSignature().getArgCount())
285             throw new InvalidTypeException("Incorrect number of arguments in " + this.asString());
286 
287         // Return the type of the highest ranking numeric arg
288         int highestRank = -1;
289         for (int i = 0; i < this.getTypeSignature().getArgCount(); i++) {
290 
291             final Class<? extends GenericValue> clazz = this.getExprArg(i).validateTypes(this, false);
292             this.validateParentClass(this.getTypeSignature().getArg(i), clazz);
293 
294             final int rank = NumericType.getTypeRanking(clazz);
295             if (rank > highestRank) {
296                 highestRank = rank;
297                 this.highestRankingNumericArgFoundInValidate = clazz;
298             }
299         }
300 
301         return this.getHighestRankingNumericArgFoundInValidate();
302     }
303 
304     public GenericValue getOptimizedValue() throws HBqlException {
305 
306         this.optimizeAllArgs();
307 
308         if (!this.isAConstant())
309             return this;
310 
311         try {
312             final Object obj = this.getValue(null, null);
313 
314             if (this.getTypeSignature().getReturnType() == BooleanValue.class
315                 || this.getTypeSignature().getReturnType() == StringValue.class
316                 || this.getTypeSignature().getReturnType() == DateValue.class)
317                 return this.getTypeSignature().newLiteral(obj);
318 
319             if (TypeSupport.isParentClass(NumberValue.class, this.getTypeSignature().getReturnType())) {
320 
321                 if (obj instanceof Short)
322                     return new ShortLiteral((Short)obj);
323 
324                 if (obj instanceof Integer)
325                     return new IntegerLiteral((Integer)obj);
326 
327                 if (obj instanceof Long)
328                     return new LongLiteral((Long)obj);
329 
330                 if (obj instanceof Float)
331                     return new FloatLiteral((Float)obj);
332 
333                 if (obj instanceof Double)
334                     return new DoubleLiteral((Double)obj);
335             }
336             throw new InternalErrorException(this.getTypeSignature().getReturnType().getSimpleName());
337         }
338         catch (ResultMissingColumnException e) {
339             throw new InternalErrorException("Missing column: " + e.getMessage());
340         }
341         catch (NullColumnValueException e) {
342             throw new InternalErrorException("Null value: " + e.getMessage());
343         }
344     }
345 
346     public String asString() {
347 
348         final StringBuilder sbuf = new StringBuilder("(");
349 
350         boolean first = true;
351         for (final GenericValue val : this.getGenericValueList()) {
352             if (!first)
353                 sbuf.append(", ");
354             sbuf.append(val.asString());
355             first = false;
356         }
357 
358         sbuf.append(")");
359 
360         return sbuf.toString();
361     }
362 
363     public void validateParentClass(final Class<? extends GenericValue> parentClass,
364                                     final Class<? extends GenericValue>... childrenClasses) throws InvalidTypeException {
365 
366         final List<Class<? extends GenericValue>> classList = Lists.newArrayList();
367 
368         for (final Class<? extends GenericValue> childClass : childrenClasses) {
369 
370             if (childClass != null) {
371                 if (TypeSupport.isParentClass(NumberValue.class, parentClass)) {
372                     if (!TypeSupport.isParentClass(NumberValue.class, childClass)) {
373                         classList.add(childClass);
374                     }
375                     else {
376                         if (!NumericType.isAssignable(parentClass, childClass))
377                             classList.add(childClass);
378                     }
379                 }
380                 else {
381                     if (!parentClass.isAssignableFrom(childClass))
382                         classList.add(childClass);
383                 }
384             }
385         }
386 
387         if (classList.size() > 0) {
388             final StringBuilder sbuf = new StringBuilder("Expecting type " + parentClass.getSimpleName()
389                                                          + " but encountered type"
390                                                          + ((classList.size() > 1) ? "s" : "") + " ");
391             boolean first = true;
392             for (final Class clazz : classList) {
393                 if (!first)
394                     sbuf.append(", ");
395                 sbuf.append(clazz.getSimpleName());
396                 first = false;
397             }
398 
399             sbuf.append(" in expression: " + this.asString());
400 
401             throw new InvalidTypeException(sbuf.toString());
402         }
403     }
404 
405     public void throwInvalidTypeException(final Class<? extends GenericValue>... clazzes) throws InvalidTypeException {
406 
407         final List<Class> classList = Lists.newArrayList();
408 
409         for (final Class clazz : clazzes)
410             if (clazz != null)
411                 classList.add(clazz);
412 
413         final StringBuilder sbuf = new StringBuilder("Invalid type");
414         sbuf.append(((classList.size() > 1) ? "s " : " "));
415 
416         boolean first = true;
417         for (final Class<? extends GenericValue> clazz : clazzes) {
418             if (!first)
419                 sbuf.append(", ");
420             sbuf.append(clazz.getSimpleName());
421             first = false;
422         }
423         sbuf.append(" in expression " + this.asString());
424 
425         throw new InvalidTypeException(sbuf.toString());
426     }
427 
428     final List<Class<? extends GenericValue>> types = Arrays.asList(StringValue.class,
429                                                                     NumberValue.class,
430                                                                     DateValue.class,
431                                                                     BooleanValue.class,
432                                                                     ObjectValue.class);
433 
434     protected Class<? extends GenericValue> getGenericValueClass(final Class<? extends GenericValue> clazz) throws InvalidTypeException {
435 
436         for (final Class<? extends GenericValue> type : types)
437             if (TypeSupport.isParentClass(type, clazz))
438                 return type;
439 
440         this.throwInvalidTypeException(clazz);
441 
442         return null;
443     }
444 
445     public Filter getFilter() throws HBqlException {
446         throw new InvalidServerFilterException();
447     }
448 }