View Javadoc
1   /*
2   Copyright (c) 2018 James Ahlborn
3   
4   Licensed under the Apache License, Version 2.0 (the "License");
5   you may not use this file except in compliance with the License.
6   You may obtain a copy of the License at
7   
8       http://www.apache.org/licenses/LICENSE-2.0
9   
10  Unless required by applicable law or agreed to in writing, software
11  distributed under the License is distributed on an "AS IS" BASIS,
12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  See the License for the specific language governing permissions and
14  limitations under the License.
15  */
16  
17  package com.healthmarketscience.jackcess.impl;
18  
19  import java.io.IOException;
20  import java.math.BigDecimal;
21  import java.text.DecimalFormat;
22  import java.time.LocalDateTime;
23  import java.time.ZoneId;
24  import java.time.format.DateTimeFormatter;
25  import java.util.Collection;
26  import java.util.EnumMap;
27  import java.util.Locale;
28  import java.util.Map;
29  import javax.script.Bindings;
30  
31  import com.healthmarketscience.jackcess.DataType;
32  import com.healthmarketscience.jackcess.JackcessException;
33  import com.healthmarketscience.jackcess.expr.EvalContext;
34  import com.healthmarketscience.jackcess.expr.EvalException;
35  import com.healthmarketscience.jackcess.expr.Expression;
36  import com.healthmarketscience.jackcess.expr.Identifier;
37  import com.healthmarketscience.jackcess.expr.LocaleContext;
38  import com.healthmarketscience.jackcess.expr.NumericConfig;
39  import com.healthmarketscience.jackcess.expr.TemporalConfig;
40  import com.healthmarketscience.jackcess.expr.Value;
41  import com.healthmarketscience.jackcess.impl.expr.Expressionator;
42  import com.healthmarketscience.jackcess.impl.expr.ValueSupport;
43  
44  /**
45   *
46   * @author James Ahlborn
47   */
48  public abstract class BaseEvalContext implements EvalContext
49  {
50    /** map of all non-string data types */
51    private static final Map<DataType,Value.Type> TYPE_MAP =
52      new EnumMap<DataType,Value.Type>(DataType.class);
53  
54    static {
55      TYPE_MAP.put(DataType.BOOLEAN,Value.Type.LONG);
56      TYPE_MAP.put(DataType.BYTE,Value.Type.LONG);
57      TYPE_MAP.put(DataType.INT,Value.Type.LONG);
58      TYPE_MAP.put(DataType.LONG,Value.Type.LONG);
59      TYPE_MAP.put(DataType.MONEY,Value.Type.DOUBLE);
60      TYPE_MAP.put(DataType.FLOAT,Value.Type.DOUBLE);
61      TYPE_MAP.put(DataType.DOUBLE,Value.Type.DOUBLE);
62      TYPE_MAP.put(DataType.SHORT_DATE_TIME,Value.Type.DATE_TIME);
63      TYPE_MAP.put(DataType.NUMERIC,Value.Type.BIG_DEC);
64      TYPE_MAP.put(DataType.BIG_INT,Value.Type.BIG_DEC);
65    }
66  
67    private final DBEvalContext _dbCtx;
68    private Expression _expr;
69  
70    protected BaseEvalContext(DBEvalContext dbCtx) {
71      _dbCtx = dbCtx;
72    }
73  
74    void setExpr(Expressionator.Type exprType, String exprStr) {
75      _expr = new RawExpr(exprType, exprStr);
76    }
77  
78    protected DatabaseImpl getDatabase() {
79      return _dbCtx.getDatabase();
80    }
81  
82    @Override
83    public Locale getLocale() {
84      return _dbCtx.getLocale();
85    }
86  
87    @Override
88    public TemporalConfig getTemporalConfig() {
89      return _dbCtx.getTemporalConfig();
90    }
91  
92    @Override
93    public DateTimeFormatter createDateFormatter(String formatStr) {
94      return _dbCtx.createDateFormatter(formatStr);
95    }
96  
97    @Override
98    public ZoneId getZoneId() {
99      return _dbCtx.getZoneId();
100   }
101 
102   @Override
103   public NumericConfig getNumericConfig() {
104     return _dbCtx.getNumericConfig();
105   }
106 
107   @Override
108   public DecimalFormat createDecimalFormat(String formatStr) {
109     return _dbCtx.createDecimalFormat(formatStr);
110   }
111 
112   @Override
113   public float getRandom(Integer seed) {
114     return _dbCtx.getRandom(seed);
115   }
116 
117   @Override
118   public Value.Type getResultType() {
119     return null;
120   }
121 
122   @Override
123   public Value getThisColumnValue() {
124     throw new UnsupportedOperationException();
125   }
126 
127   @Override
128   public Value getIdentifierValue(Identifier identifier) {
129     throw new UnsupportedOperationException();
130   }
131 
132   @Override
133   public Bindings getBindings() {
134     return _dbCtx.getBindings();
135   }
136 
137   @Override
138   public Object get(String key) {
139     return _dbCtx.getBindings().get(key);
140   }
141 
142   @Override
143   public void put(String key, Object value) {
144     _dbCtx.getBindings().put(key, value);
145   }
146 
147   public Object eval() throws IOException {
148     try {
149       return _expr.eval(this);
150     } catch(Exception e) {
151       String msg = withErrorContext(e.getMessage());
152       throw new JackcessException(msg, e);
153     }
154   }
155 
156   public void collectIdentifiers(Collection<Identifier> identifiers) {
157     _expr.collectIdentifiers(identifiers);
158   }
159 
160   @Override
161   public String toString() {
162     return _expr.toString();
163   }
164 
165   protected Value toValue(Object val, DataType dType) {
166     try {
167       // expression engine always uses LocalDateTime, so force that date/time
168       // type
169       val = ColumnImpl.toInternalValue(dType, val, getDatabase(),
170                                        ColumnImpl.LDT_DATE_TIME_FACTORY);
171       if(val == null) {
172         return ValueSupport.NULL_VAL;
173       }
174 
175       Value.Type vType = toValueType(dType);
176       switch(vType) {
177       case STRING:
178         return ValueSupport.toValue(val.toString());
179       case DATE:
180       case TIME:
181       case DATE_TIME:
182         return ValueSupport.toValue(vType, (LocalDateTime)val);
183       case LONG:
184         Integer i = ((val instanceof Integer) ? (Integer)val :
185                      ((Number)val).intValue());
186         return ValueSupport.toValue(i);
187       case DOUBLE:
188         Double d = ((val instanceof Double) ? (Double)val :
189                     ((Number)val).doubleValue());
190         return ValueSupport.toValue(d);
191       case BIG_DEC:
192         BigDecimal bd = ColumnImpl.toBigDecimal(val, getDatabase());
193         return ValueSupport.toValue(bd);
194       default:
195         throw new RuntimeException("Unexpected type " + vType);
196       }
197     } catch(IOException e) {
198       throw new EvalException("Failed converting value to type " + dType, e);
199     }
200   }
201 
202   public static Value.Type toValueType(DataType dType) {
203     Value.Type type = TYPE_MAP.get(dType);
204     return ((type == null) ? Value.Type.STRING : type);
205   }
206 
207   protected abstract String withErrorContext(String msg);
208 
209   private class RawExpr implements Expression
210   {
211     private final Expressionator.Type _exprType;
212     private final String _exprStr;
213 
214     private RawExpr(Expressionator.Type exprType, String exprStr) {
215       _exprType = exprType;
216       _exprStr = exprStr;
217     }
218 
219     private Expression getExpr() {
220       // when the expression is parsed we replace the raw version
221       Expression expr = Expressionator.parse(
222           _exprType, _exprStr, getResultType(), _dbCtx);
223       _expr = expr;
224       return expr;
225     }
226 
227     @Override
228     public Object eval(EvalContext ctx) {
229       return getExpr().eval(ctx);
230     }
231 
232     @Override
233     public String toDebugString(LocaleContext ctx) {
234       return getExpr().toDebugString(ctx);
235     }
236 
237     @Override
238     public String toRawString() {
239       return _exprStr;
240     }
241 
242     @Override
243     public String toCleanString(LocaleContext ctx) {
244       return getExpr().toCleanString(ctx);
245     }
246 
247     @Override
248     public boolean isConstant() {
249       return getExpr().isConstant();
250     }
251 
252     @Override
253     public void collectIdentifiers(Collection<Identifier> identifiers) {
254       getExpr().collectIdentifiers(identifiers);
255     }
256 
257     @Override
258     public String toString() {
259       return toRawString();
260     }
261   }
262 }