View Javadoc

1   /*
2    * Copyright (c) 2011.  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     protected Class<? extends GenericValue> validateNumericTypes() throws HBqlException {
162 
163         if (this.getGenericValueList().size() != this.getTypeSignature().getArgCount())
164             throw new InvalidTypeException("Incorrect number of arguments in " + this.asString());
165 
166         // Return the type of the highest ranking numeric arg
167         int highestRank = -1;
168         for (int i = 0; i < this.getTypeSignature().getArgCount(); i++) {
169 
170             final Class<? extends GenericValue> clazz = this.getExprArg(i).validateTypes(this, false);
171             this.validateParentClass(this.getTypeSignature().getArg(i), clazz);
172 
173             final int rank = NumericType.getTypeRanking(clazz);
174             if (rank > highestRank) {
175                 highestRank = rank;
176                 this.highestRankingNumericArgFoundInValidate = clazz;
177             }
178         }
179 
180         return this.getHighestRankingNumericArgFoundInValidate();
181     }
182 
183     public boolean isAConstant() {
184 
185         if (this.getGenericValueList().size() == 0)
186             return false;
187 
188         for (final GenericValue val : this.getGenericValueList())
189             if (!val.isAConstant())
190                 return false;
191 
192         return true;
193     }
194 
195     public boolean isDefaultKeyword() {
196         return false;
197     }
198 
199     public boolean isAnAggregateValue() {
200         return false;
201     }
202 
203     public void initAggregateValue(final AggregateValue aggregateValue) throws HBqlException {
204         throw new InternalErrorException("Not applicable");
205     }
206 
207     public void applyResultToAggregateValue(final AggregateValue aggregateValue, final Result result) throws HBqlException, ResultMissingColumnException, NullColumnValueException {
208         throw new InternalErrorException("Not applicable");
209     }
210 
211     public boolean hasAColumnReference() {
212         for (final GenericValue val : this.getGenericValueList())
213             if (val.hasAColumnReference())
214                 return true;
215         return false;
216     }
217 
218     public boolean isAColumnReference() {
219         return false;
220     }
221 
222     public void reset() {
223         for (final GenericValue val : this.getGenericValueList())
224             val.reset();
225     }
226 
227     public void setExpressionContext(final MultipleExpressionContext expressionContext) throws HBqlException {
228 
229         this.expressionContext = expressionContext;
230 
231         for (final GenericValue val : this.getGenericValueList())
232             val.setExpressionContext(expressionContext);
233     }
234 
235     protected MultipleExpressionContext getExpressionContext() {
236         return this.expressionContext;
237     }
238 
239     private AtomicBoolean getAllArgsOptimized() {
240         return this.allArgsOptimized;
241     }
242 
243     protected void optimizeAllArgs() throws HBqlException {
244         if (!this.getAllArgsOptimized().get())
245             synchronized (this) {
246                 if (!this.getAllArgsOptimized().get()) {
247                     for (int i = 0; i < this.getGenericValueList().size(); i++)
248                         this.setArg(i, this.getExprArg(i).getOptimizedValue());
249                     this.getAllArgsOptimized().set(true);
250                 }
251             }
252     }
253 
254     protected Filter newSingleColumnValueFilter(final ColumnAttrib attrib,
255                                                 final CompareFilter.CompareOp compareOp,
256                                                 final WritableByteArrayComparable comparator) throws HBqlException {
257 
258         // Bail if expression uses a row key.
259         if (attrib.isAKeyAttrib())
260             throw new InvalidServerFilterException("Cannot use a key attribute");
261 
262         final SingleColumnValueFilter filter = new SingleColumnValueFilter(attrib.getFamilyNameAsBytes(),
263                                                                            attrib.getColumnNameAsBytes(),
264                                                                            compareOp,
265                                                                            comparator);
266         filter.setFilterIfMissing(true);
267         return filter;
268     }
269 
270     protected Object getConstantValue(final int pos) throws HBqlException {
271         try {
272             return this.getExprArg(pos).getValue(null, null);
273         }
274         catch (ResultMissingColumnException e) {
275             throw new InternalErrorException("Missing column: " + e.getMessage());
276         }
277         catch (NullColumnValueException e) {
278             throw new InternalErrorException("Null value: " + e.getMessage());
279         }
280     }
281 
282     public GenericValue getExprArg(final int i) {
283         return this.getGenericValueList().get(i);
284     }
285 
286     public void setArg(final int i, final GenericValue val) {
287         this.getGenericValueList().set(i, val);
288     }
289 
290     public Class<? extends GenericValue> validateTypes(final GenericValue parentExpr,
291                                                        final boolean allowCollections) throws HBqlException {
292 
293         if (this.getGenericValueList().size() != this.getTypeSignature().getArgCount())
294             throw new InvalidTypeException("Incorrect number of arguments in " + this.asString());
295 
296         final FunctionTypeSignature typeSignature = this.getTypeSignature();
297 
298         for (int i = 0; i < typeSignature.getArgCount(); i++)
299             this.validateParentClass(typeSignature.getArg(i), this.getExprArg(i).validateTypes(this, false));
300 
301         return typeSignature.getReturnType();
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 String getInvalidTypeMsg(final Class<? extends GenericValue>... classes) {
406 
407         final List<Class> classList = Lists.newArrayList();
408 
409         for (final Class clazz : classes)
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 : classes) {
418             if (!first)
419                 sbuf.append(", ");
420             sbuf.append(clazz.getSimpleName());
421             first = false;
422         }
423         sbuf.append(" in expression " + this.asString());
424 
425         return 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         throw new InvalidTypeException(this.getInvalidTypeMsg(clazz));
441     }
442 
443     public Filter getFilter() throws HBqlException {
444         throw new InvalidServerFilterException();
445     }
446 }