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.Map;
28  import javax.script.Bindings;
29  
30  import com.healthmarketscience.jackcess.DataType;
31  import com.healthmarketscience.jackcess.JackcessException;
32  import com.healthmarketscience.jackcess.expr.EvalContext;
33  import com.healthmarketscience.jackcess.expr.EvalException;
34  import com.healthmarketscience.jackcess.expr.Expression;
35  import com.healthmarketscience.jackcess.expr.Identifier;
36  import com.healthmarketscience.jackcess.expr.LocaleContext;
37  import com.healthmarketscience.jackcess.expr.NumericConfig;
38  import com.healthmarketscience.jackcess.expr.TemporalConfig;
39  import com.healthmarketscience.jackcess.expr.Value;
40  import com.healthmarketscience.jackcess.impl.expr.Expressionator;
41  import com.healthmarketscience.jackcess.impl.expr.ValueSupport;
42  
43  /**
44   *
45   * @author James Ahlborn
46   */
47  public abstract class BaseEvalContext implements EvalContext
48  {
49    /** map of all non-string data types */
50    private static final Map<DataType,Value.Type> TYPE_MAP =
51      new EnumMap<DataType,Value.Type>(DataType.class);
52  
53    static {
54      TYPE_MAP.put(DataType.BOOLEAN,Value.Type.LONG);
55      TYPE_MAP.put(DataType.BYTE,Value.Type.LONG);
56      TYPE_MAP.put(DataType.INT,Value.Type.LONG);
57      TYPE_MAP.put(DataType.LONG,Value.Type.LONG);
58      TYPE_MAP.put(DataType.MONEY,Value.Type.DOUBLE);
59      TYPE_MAP.put(DataType.FLOAT,Value.Type.DOUBLE);
60      TYPE_MAP.put(DataType.DOUBLE,Value.Type.DOUBLE);
61      TYPE_MAP.put(DataType.SHORT_DATE_TIME,Value.Type.DATE_TIME);
62      TYPE_MAP.put(DataType.NUMERIC,Value.Type.BIG_DEC);
63      TYPE_MAP.put(DataType.BIG_INT,Value.Type.BIG_DEC);
64    }
65  
66    private final DBEvalContext _dbCtx;
67    private Expression _expr;
68  
69    protected BaseEvalContext(DBEvalContext dbCtx) {
70      _dbCtx = dbCtx;
71    }
72  
73    void setExpr(Expressionator.Type exprType, String exprStr) {
74      _expr = new RawExpr(exprType, exprStr);
75    }
76  
77    protected DatabaseImpl getDatabase() {
78      return _dbCtx.getDatabase();
79    }
80  
81    @Override
82    public TemporalConfig getTemporalConfig() {
83      return _dbCtx.getTemporalConfig();
84    }
85  
86    @Override
87    public DateTimeFormatter createDateFormatter(String formatStr) {
88      return _dbCtx.createDateFormatter(formatStr);
89    }
90  
91    @Override
92    public ZoneId getZoneId() {
93      return _dbCtx.getZoneId();
94    }
95  
96    @Override
97    public NumericConfig getNumericConfig() {
98      return _dbCtx.getNumericConfig();
99    }
100 
101   @Override
102   public DecimalFormat createDecimalFormat(String formatStr) {
103     return _dbCtx.createDecimalFormat(formatStr);
104   }
105 
106   @Override
107   public float getRandom(Integer seed) {
108     return _dbCtx.getRandom(seed);
109   }
110 
111   @Override
112   public Value.Type getResultType() {
113     return null;
114   }
115 
116   @Override
117   public Value getThisColumnValue() {
118     throw new UnsupportedOperationException();
119   }
120 
121   @Override
122   public Value getIdentifierValue(Identifier identifier) {
123     throw new UnsupportedOperationException();
124   }
125 
126   @Override
127   public Bindings getBindings() {
128     return _dbCtx.getBindings();
129   }
130 
131   @Override
132   public Object get(String key) {
133     return _dbCtx.getBindings().get(key);
134   }
135 
136   @Override
137   public void put(String key, Object value) {
138     _dbCtx.getBindings().put(key, value);
139   }
140 
141   public Object eval() throws IOException {
142     try {
143       return _expr.eval(this);
144     } catch(Exception e) {
145       String msg = withErrorContext(e.getMessage());
146       throw new JackcessException(msg, e);
147     }
148   }
149 
150   public void collectIdentifiers(Collection<Identifier> identifiers) {
151     _expr.collectIdentifiers(identifiers);
152   }
153 
154   @Override
155   public String toString() {
156     return _expr.toString();
157   }
158 
159   protected Value toValue(Object val, DataType dType) {
160     try {
161       // expression engine always uses LocalDateTime, so force that date/time
162       // type
163       val = ColumnImpl.toInternalValue(dType, val, getDatabase(),
164                                        ColumnImpl.LDT_DATE_TIME_FACTORY);
165       if(val == null) {
166         return ValueSupport.NULL_VAL;
167       }
168 
169       Value.Type vType = toValueType(dType);
170       switch(vType) {
171       case STRING:
172         return ValueSupport.toValue(val.toString());
173       case DATE:
174       case TIME:
175       case DATE_TIME:
176         return ValueSupport.toValue(vType, (LocalDateTime)val);
177       case LONG:
178         Integer i = ((val instanceof Integer) ? (Integer)val :
179                      ((Number)val).intValue());
180         return ValueSupport.toValue(i);
181       case DOUBLE:
182         Double d = ((val instanceof Double) ? (Double)val :
183                     ((Number)val).doubleValue());
184         return ValueSupport.toValue(d);
185       case BIG_DEC:
186         BigDecimal bd = ColumnImpl.toBigDecimal(val, getDatabase());
187         return ValueSupport.toValue(bd);
188       default:
189         throw new RuntimeException("Unexpected type " + vType);
190       }
191     } catch(IOException e) {
192       throw new EvalException("Failed converting value to type " + dType, e);
193     }
194   }
195 
196   public static Value.Type toValueType(DataType dType) {
197     Value.Type type = TYPE_MAP.get(dType);
198     return ((type == null) ? Value.Type.STRING : type);
199   }
200 
201   protected abstract String withErrorContext(String msg);
202 
203   private class RawExpr implements Expression
204   {
205     private final Expressionator.Type _exprType;
206     private final String _exprStr;
207 
208     private RawExpr(Expressionator.Type exprType, String exprStr) {
209       _exprType = exprType;
210       _exprStr = exprStr;
211     }
212 
213     private Expression getExpr() {
214       // when the expression is parsed we replace the raw version
215       Expression expr = Expressionator.parse(
216           _exprType, _exprStr, getResultType(), _dbCtx);
217       _expr = expr;
218       return expr;
219     }
220 
221     @Override
222     public Object eval(EvalContext ctx) {
223       return getExpr().eval(ctx);
224     }
225 
226     @Override
227     public String toDebugString(LocaleContext ctx) {
228       return getExpr().toDebugString(ctx);
229     }
230 
231     @Override
232     public String toRawString() {
233       return _exprStr;
234     }
235 
236     @Override
237     public String toCleanString(LocaleContext ctx) {
238       return getExpr().toCleanString(ctx);
239     }
240 
241     @Override
242     public boolean isConstant() {
243       return getExpr().isConstant();
244     }
245 
246     @Override
247     public void collectIdentifiers(Collection<Identifier> identifiers) {
248       getExpr().collectIdentifiers(identifiers);
249     }
250 
251     @Override
252     public String toString() {
253       return toRawString();
254     }
255   }
256 }