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.compare;
22  
23  import org.apache.commons.logging.Log;
24  import org.apache.commons.logging.LogFactory;
25  import org.apache.expreval.client.NullColumnValueException;
26  import org.apache.expreval.client.ResultMissingColumnException;
27  import org.apache.expreval.expr.Operator;
28  import org.apache.expreval.expr.node.GenericValue;
29  import org.apache.expreval.expr.node.NumberValue;
30  import org.apache.expreval.expr.var.DelegateColumn;
31  import org.apache.expreval.expr.var.GenericColumn;
32  import org.apache.hadoop.hbase.filter.CompareFilter;
33  import org.apache.hadoop.hbase.filter.Filter;
34  import org.apache.hadoop.hbase.filter.WritableByteArrayComparable;
35  import org.apache.hadoop.hbase.hbql.client.HBqlException;
36  import org.apache.hadoop.hbase.hbql.impl.HConnectionImpl;
37  import org.apache.hadoop.hbase.hbql.impl.Utils;
38  import org.apache.hadoop.hbase.hbql.io.IO;
39  import org.apache.hadoop.hbase.hbql.mapping.FieldType;
40  
41  import java.io.DataInput;
42  import java.io.DataOutput;
43  import java.io.IOException;
44  
45  public class NumberCompare extends GenericCompare {
46  
47      private static final Log LOG = LogFactory.getLog(NumberCompare.class);
48  
49      public NumberCompare(final GenericValue arg0, final Operator operator, final GenericValue arg1) {
50          super(arg0, operator, arg1);
51      }
52  
53      public Class<? extends GenericValue> validateTypes(final GenericValue parentExpr,
54                                                         final boolean allowCollections) throws HBqlException {
55          return this.validateType(NumberValue.class);
56      }
57  
58      public Boolean getValue(final HConnectionImpl conn, final Object object) throws HBqlException,
59                                                                                      ResultMissingColumnException,
60                                                                                      NullColumnValueException {
61          final Object obj0 = this.getValue(0, conn, object);
62          final Object obj1 = this.getValue(1, conn, object);
63  
64          this.validateNumericArgTypes(obj0, obj1);
65  
66          if (!this.useDecimal()) {
67  
68              final long val0 = ((Number)obj0).longValue();
69              final long val1 = ((Number)obj1).longValue();
70  
71              switch (this.getOperator()) {
72                  case EQ:
73                      return val0 == val1;
74                  case GT:
75                      return val0 > val1;
76                  case GTEQ:
77                      return val0 >= val1;
78                  case LT:
79                      return val0 < val1;
80                  case LTEQ:
81                      return val0 <= val1;
82                  case NOTEQ:
83                      return val0 != val1;
84                  default:
85                      throw new HBqlException("Invalid operator: " + this.getOperator());
86              }
87          }
88          else {
89  
90              final double val0 = ((Number)obj0).doubleValue();
91              final double val1 = ((Number)obj1).doubleValue();
92  
93              switch (this.getOperator()) {
94                  case EQ:
95                      return val0 == val1;
96                  case GT:
97                      return val0 > val1;
98                  case GTEQ:
99                      return val0 >= val1;
100                 case LT:
101                     return val0 < val1;
102                 case LTEQ:
103                     return val0 <= val1;
104                 case NOTEQ:
105                     return val0 != val1;
106                 default:
107                     throw new HBqlException("Invalid operator: " + this.getOperator());
108             }
109         }
110     }
111 
112     public Filter getFilter() throws HBqlException {
113 
114         this.validateArgsForCompareFilter();
115 
116         final GenericColumn<? extends GenericValue> column;
117         final Object constant;
118         final CompareFilter.CompareOp compareOp;
119         final WritableByteArrayComparable comparator;
120 
121         if (this.getExprArg(0).isAColumnReference()) {
122             column = ((DelegateColumn)this.getExprArg(0)).getTypedColumn();
123             constant = this.getConstantValue(1);
124             compareOp = this.getOperator().getCompareOpLeft();
125         }
126         else {
127             column = ((DelegateColumn)this.getExprArg(1)).getTypedColumn();
128             constant = this.getConstantValue(0);
129             compareOp = this.getOperator().getCompareOpRight();
130         }
131 
132         this.validateNumericArgTypes(constant);
133 
134         if (!this.useDecimal()) {
135             final long val = ((Number)constant).longValue();
136             comparator = new LongComparable(column.getColumnAttrib().getFieldType(), val);
137         }
138         else {
139             final double val = ((Number)constant).doubleValue();
140             comparator = new DoubleComparable(column.getColumnAttrib().getFieldType(), val);
141         }
142 
143         return this.newSingleColumnValueFilter(column.getColumnAttrib(), compareOp, comparator);
144     }
145 
146     private static abstract class NumberComparable<T> extends GenericComparable<T> {
147 
148         private FieldType fieldType;
149 
150         protected void setFieldType(final FieldType fieldType) {
151             this.fieldType = fieldType;
152         }
153 
154         protected FieldType getFieldType() {
155             return this.fieldType;
156         }
157 
158         protected void setValueInBytes(final Number val) throws IOException {
159             try {
160                 this.setValueInBytes(IO.getSerialization().getNumberEqualityBytes(this.getFieldType(), val));
161             }
162             catch (HBqlException e) {
163                 throw new IOException(e.getMessage());
164             }
165         }
166     }
167 
168     private static class LongComparable extends NumberComparable<Long> {
169 
170         public LongComparable() {
171         }
172 
173         public LongComparable(final FieldType fieldType, final long value) {
174             this.setFieldType(fieldType);
175             this.setTypedValue(value);
176         }
177 
178         public int compareTo(final byte[] bytes) {
179 
180             if (this.equalValues(bytes))
181                 return 0;
182 
183             try {
184                 long columnValue = IO.getSerialization().getNumberFromBytes(this.getFieldType(), bytes).longValue();
185                 // Test for equality again in case the byte[] lengths were different above
186                 final long val = this.getTypedValue();
187                 if (columnValue == val)
188                     return 0;
189                 else
190                     return (columnValue > val) ? -1 : 1;
191             }
192             catch (HBqlException e) {
193                 e.printStackTrace();
194                 Utils.logException(LOG, e);
195                 return 1;
196             }
197         }
198 
199         public void write(final DataOutput dataOutput) throws IOException {
200             dataOutput.writeUTF(this.getFieldType().name());
201             dataOutput.writeLong(this.getTypedValue());
202         }
203 
204         public void readFields(final DataInput dataInput) throws IOException {
205             this.setFieldType(FieldType.valueOf(dataInput.readUTF()));
206             this.setTypedValue(dataInput.readLong());
207 
208             this.setValueInBytes(this.getTypedValue());
209         }
210     }
211 
212     private static class DoubleComparable extends NumberComparable<Double> {
213 
214         public DoubleComparable() {
215         }
216 
217         public DoubleComparable(final FieldType fieldType, final double value) {
218             this.setFieldType(fieldType);
219             this.setTypedValue(value);
220         }
221 
222         public int compareTo(final byte[] bytes) {
223 
224             if (this.equalValues(bytes))
225                 return 0;
226 
227             try {
228                 double columnValue = IO.getSerialization().getNumberFromBytes(this.getFieldType(), bytes).doubleValue();
229                 // Test for equality again in case the byte[] lengths were different above
230                 final double val = this.getTypedValue();
231                 if (columnValue == val)
232                     return 0;
233                 else
234                     return (columnValue > val) ? -1 : 1;
235             }
236             catch (HBqlException e) {
237                 e.printStackTrace();
238                 Utils.logException(LOG, e);
239                 return 1;
240             }
241         }
242 
243         public void write(final DataOutput dataOutput) throws IOException {
244             dataOutput.writeUTF(this.getFieldType().name());
245             dataOutput.writeDouble(this.getTypedValue());
246         }
247 
248         public void readFields(final DataInput dataInput) throws IOException {
249             this.setFieldType(FieldType.valueOf(dataInput.readUTF()));
250             this.setTypedValue(dataInput.readDouble());
251 
252             this.setValueInBytes(this.getTypedValue());
253         }
254     }
255 }