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.node.GenericValue;
27  import org.apache.expreval.expr.node.NumberValue;
28  import org.apache.expreval.expr.node.ObjectValue;
29  import org.apache.expreval.expr.var.GenericColumn;
30  import org.apache.expreval.expr.var.NamedParameter;
31  import org.apache.hadoop.hbase.hbql.client.HBqlException;
32  import org.apache.hadoop.hbase.hbql.impl.HConnectionImpl;
33  import org.apache.hadoop.hbase.hbql.impl.InvalidTypeException;
34  import org.apache.hadoop.hbase.hbql.mapping.ColumnAttrib;
35  import org.apache.hadoop.hbase.hbql.mapping.HRecordResultAccessor;
36  import org.apache.hadoop.hbase.hbql.mapping.Mapping;
37  import org.apache.hadoop.hbase.hbql.mapping.MappingContext;
38  import org.apache.hadoop.hbase.hbql.mapping.ResultAccessor;
39  import org.apache.hadoop.hbase.hbql.mapping.TableMapping;
40  import org.apache.hadoop.hbase.hbql.util.Lists;
41  import org.apache.hadoop.hbase.hbql.util.Maps;
42  
43  import java.io.Serializable;
44  import java.util.List;
45  import java.util.Map;
46  
47  public abstract class MultipleExpressionContext implements Serializable {
48  
49      private static final long serialVersionUID = 1L;
50  
51      private boolean needsTypeValidation = true;
52      private boolean needsOptimization   = true;
53      private boolean needsContextSetting = true;
54  
55      private final List<GenericValue>                expressions        = Lists.newArrayList();
56      private final List<GenericColumn>               columnsUsedInExprs = Lists.newArrayList();
57      private final List<ColumnAttrib>                attribsUsedInExprs = Lists.newArrayList();
58      private final List<NamedParameter>              namedParamList     = Lists.newArrayList();
59      private final Map<String, List<NamedParameter>> namedParamMap      = Maps.newHashMap();
60  
61      private MappingContext mappingContext = null;
62      private TypeSignature  typeSignature  = null;
63  
64      protected MultipleExpressionContext() {
65      }
66  
67      protected MultipleExpressionContext(final TypeSignature typeSignature, final GenericValue... vals) {
68          this.typeSignature = typeSignature;
69          if (vals != null) {
70              for (final GenericValue val : vals)
71                  this.addExpression(val);
72          }
73      }
74  
75      public abstract String asString();
76  
77      public abstract boolean useResultData();
78  
79      public abstract boolean allowColumns();
80  
81      public List<GenericColumn> getColumnsUsedInExpr() {
82          return this.columnsUsedInExprs;
83      }
84  
85      public List<ColumnAttrib> getAttribsUsedInExpr() {
86          return this.attribsUsedInExprs;
87      }
88  
89      public void addExpression(final GenericValue genericValue) {
90          this.getExpressionList().add(genericValue);
91      }
92  
93      public Map<String, List<NamedParameter>> getNamedParamMap() {
94          return this.namedParamMap;
95      }
96  
97      protected List<GenericValue> getExpressionList() {
98          return this.expressions;
99      }
100 
101     private TypeSignature getTypeSignature() {
102         return this.typeSignature;
103     }
104 
105     public MappingContext getMappingContext() {
106         return this.mappingContext;
107     }
108 
109     public Mapping getMapping() throws HBqlException {
110         return this.getMappingContext().getMapping();
111     }
112 
113     public TableMapping getTableMapping() {
114         return this.getMappingContext().getTableMapping();
115     }
116 
117     public ResultAccessor getResultAccessor() throws HBqlException {
118         return this.getMappingContext().getResultAccessor();
119     }
120 
121     public void setMappingContext(final MappingContext mappingContext) throws HBqlException {
122         this.mappingContext = mappingContext;
123 
124         if (this.getMappingContext() != null && this.getMappingContext().getResultAccessor() == null)
125             this.getMappingContext().setResultAccessor(new HRecordResultAccessor(mappingContext));
126 
127         this.setExpressionListContext();
128     }
129 
130     private synchronized void setExpressionListContext() throws HBqlException {
131         if (this.needsContextSetting()) {
132             for (final GenericValue val : this.getExpressionList())
133                 val.setExpressionContext(this);
134             this.setNeedsContextSetting(false);
135         }
136     }
137 
138     protected GenericValue getGenericValue(final int i) {
139         return this.getExpressionList().get(i);
140     }
141 
142     public Object evaluate(final HConnectionImpl conn,
143                            final int i,
144                            final boolean allowColumns,
145                            final boolean allowCollections,
146                            final Object object) throws HBqlException, ResultMissingColumnException, NullColumnValueException {
147         this.validateTypes(allowColumns, allowCollections);
148         this.optimize();
149         final GenericValue genericValue = this.getGenericValue(i);
150         return genericValue.getValue(conn, object);
151     }
152 
153     public Object evaluateConstant(final int i, final boolean allowCollections) throws HBqlException {
154         try {
155             return this.evaluate(null, i, false, allowCollections, null);
156         }
157         catch (ResultMissingColumnException e) {
158             throw new InternalErrorException("Missing column: " + e.getMessage());
159         }
160         catch (NullColumnValueException e) {
161             throw new InternalErrorException("Null value: " + e.getMessage());
162         }
163     }
164 
165     public void reset() {
166 
167         this.setNeedsTypeValidation(true);
168         this.setNeedsOptimization(true);
169 
170         for (final GenericValue val : this.getExpressionList())
171             val.reset();
172     }
173 
174     protected void setGenericValue(final int i, final GenericValue treeRoot) {
175         this.getExpressionList().set(i, treeRoot);
176     }
177 
178     public void optimize() throws HBqlException {
179         if (this.needsOptimization()) {
180             for (int i = 0; i < this.getExpressionList().size(); i++)
181                 this.setGenericValue(i, this.getGenericValue(i).getOptimizedValue());
182             this.setNeedsOptimization(false);
183         }
184     }
185 
186     public void validateTypes(final boolean allowColumns, final boolean allowCollections) throws HBqlException {
187 
188         if (this.needsTypeValidation()) {
189 
190             if (!allowColumns && this.getColumnsUsedInExpr().size() > 0)
191                 throw new InvalidTypeException("Invalid column reference"
192                                                + (this.getColumnsUsedInExpr().size() > 1 ? "s" : "")
193                                                + " in " + this.asString());
194 
195             // Collect return types of all args
196             // This is run even if TypeSignature is null because it calls validateTypes()
197             final List<Class<? extends GenericValue>> clazzList = Lists.newArrayList();
198             for (final GenericValue val : this.getExpressionList()) {
199                 final Class<? extends GenericValue> returnType = val.validateTypes(val, allowCollections);
200                 clazzList.add(returnType);
201             }
202 
203             // Check against signature if there is one
204             if (this.getTypeSignature() != null) {
205 
206                 if (this.getExpressionList().size() != this.getTypeSignature().getArgCount())
207                     throw new InvalidTypeException("Incorrect number of variables in " + this.asString());
208 
209                 for (int i = 0; i < this.getTypeSignature().getArgCount(); i++) {
210 
211                     final Class<? extends GenericValue> parentClazz = this.getTypeSignature().getArg(i);
212                     final Class<? extends GenericValue> clazz = clazzList.get(i);
213 
214                     // See if they are both NumberValues.  If they are, then check ranks
215                     if (TypeSupport.isParentClass(NumberValue.class, parentClazz, clazz)) {
216                         final int parentRank = NumericType.getTypeRanking(parentClazz);
217                         final int clazzRank = NumericType.getTypeRanking(clazz);
218                         if (clazzRank > parentRank)
219                             throw new InvalidTypeException("Cannot assign a " + clazz.getSimpleName()
220                                                            + " value to a " + parentClazz.getSimpleName()
221                                                            + " value in " + this.asString());
222                     }
223                     else if (parentClazz == ObjectValue.class) {
224                         // Do nothing
225                     }
226                     else {
227                         if (!parentClazz.isAssignableFrom(clazz))
228                             throw new InvalidTypeException("Expecting type " + parentClazz.getSimpleName()
229                                                            + " but found type " + clazz.getSimpleName()
230                                                            + " in " + this.asString());
231                     }
232                 }
233             }
234 
235             this.setNeedsTypeValidation(false);
236         }
237     }
238 
239     public List<NamedParameter> getParameterList() {
240         return this.namedParamList;
241     }
242 
243     public void addNamedParameter(final NamedParameter param) {
244 
245         this.getParameterList().add(param);
246 
247         final String name = param.getParamName();
248         final List<NamedParameter> paramList;
249 
250         if (!this.getNamedParamMap().containsKey(name)) {
251             paramList = Lists.newArrayList();
252             this.getNamedParamMap().put(name, paramList);
253         }
254         else {
255             paramList = this.getNamedParamMap().get(name);
256         }
257 
258         paramList.add(param);
259     }
260 
261     public int setParameter(final String name, final Object val) throws HBqlException {
262 
263         final String fullname = name.startsWith(":") ? name : (":" + name);
264 
265         if (!this.getNamedParamMap().containsKey(fullname))
266             return 0;
267 
268         // Set all occurences to param value
269         final List<NamedParameter> paramList = this.getNamedParamMap().get(fullname);
270         for (final NamedParameter param : paramList)
271             param.setParameter(val);
272 
273         this.setNeedsTypeValidation(true);
274 
275         return paramList.size();
276     }
277 
278     public void addColumnToUsedList(final GenericColumn column) {
279         this.getColumnsUsedInExpr().add(column);
280         this.getAttribsUsedInExpr().add(column.getColumnAttrib());
281     }
282 
283     private boolean needsTypeValidation() {
284         return needsTypeValidation;
285     }
286 
287     private void setNeedsTypeValidation(final boolean inNeedOfTypeValidation) {
288         this.needsTypeValidation = inNeedOfTypeValidation;
289     }
290 
291     private boolean needsOptimization() {
292         return needsOptimization;
293     }
294 
295     private void setNeedsOptimization(final boolean inNeedOfOptimization) {
296         this.needsOptimization = inNeedOfOptimization;
297     }
298 
299     private boolean needsContextSetting() {
300         return needsContextSetting;
301     }
302 
303     private void setNeedsContextSetting(final boolean needsContextSetting) {
304         this.needsContextSetting = needsContextSetting;
305     }
306 }