You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Expressionator.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. /*
  2. Copyright (c) 2016 James Ahlborn
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package com.healthmarketscience.jackcess.util;
  14. import java.text.ParseException;
  15. import java.text.SimpleDateFormat;
  16. import java.util.ArrayList;
  17. import java.util.Arrays;
  18. import java.util.Collections;
  19. import java.util.Date;
  20. import java.util.HashMap;
  21. import java.util.HashSet;
  22. import java.util.Iterator;
  23. import java.util.List;
  24. import java.util.Map;
  25. import java.util.Set;
  26. import com.healthmarketscience.jackcess.Database;
  27. import com.healthmarketscience.jackcess.impl.DatabaseImpl;
  28. import static com.healthmarketscience.jackcess.util.ExpressionTokenizer.Token;
  29. import static com.healthmarketscience.jackcess.util.ExpressionTokenizer.TokenType;
  30. /**
  31. *
  32. * @author James Ahlborn
  33. */
  34. public class Expressionator
  35. {
  36. public enum Type {
  37. DEFAULT_VALUE, FIELD_VALIDATOR;
  38. }
  39. private enum WordType {
  40. OP, COMP, LOG_OP, CONST, SPEC_OP_PREFIX;
  41. }
  42. private static final String FUNC_START_DELIM = "(";
  43. private static final String FUNC_END_DELIM = ")";
  44. private static final String FUNC_PARAM_SEP = ",";
  45. private static final Map<String,WordType> WORD_TYPES = new HashMap<String,WordType>();
  46. static {
  47. setWordType(WordType.OP, "+", "-", "*", "/", "\\", "^", "&", "mod");
  48. setWordType(WordType.COMP, "<", "<=", ">", ">=", "=", "<>");
  49. setWordType(WordType.LOG_OP, "and", "or", "eqv", "not", "xor");
  50. setWordType(WordType.CONST, "true", "false", "null");
  51. setWordType(WordType.SPEC_OP_PREFIX, "is", "like", "between", "in");
  52. }
  53. private static final Expr THIS_COL_VALUE = new Expr() {
  54. @Override protected Object eval(RowContext ctx) {
  55. return ctx.getThisColumnValue();
  56. }
  57. };
  58. private static final Expr NULL_VALUE = new Expr() {
  59. @Override protected Object eval(RowContext ctx) {
  60. return null;
  61. }
  62. };
  63. private static final Expr TRUE_VALUE = new Expr() {
  64. @Override protected Object eval(RowContext ctx) {
  65. return Boolean.TRUE;
  66. }
  67. };
  68. private static final Expr FALSE_VALUE = new Expr() {
  69. @Override protected Object eval(RowContext ctx) {
  70. return Boolean.FALSE;
  71. }
  72. };
  73. private Expressionator()
  74. {
  75. }
  76. public static String testTokenize(Type exprType, String exprStr, Database db) {
  77. List<Token> tokens = trimSpaces(
  78. ExpressionTokenizer.tokenize(exprType, exprStr, (DatabaseImpl)db));
  79. if(tokens == null) {
  80. // FIXME, NULL_EXPR?
  81. return null;
  82. }
  83. return tokens.toString();
  84. }
  85. public static Expr parse(Type exprType, String exprStr, Database db) {
  86. List<Token> tokens = trimSpaces(
  87. ExpressionTokenizer.tokenize(exprType, exprStr, (DatabaseImpl)db));
  88. if(tokens == null) {
  89. // FIXME, NULL_EXPR?
  90. return null;
  91. }
  92. TokBuf buf = new TokBuf(tokens);
  93. parseExpression(exprType, buf, isSimpleExpression(buf, exprType));
  94. // FIXME
  95. return null;
  96. }
  97. private static List<Token> trimSpaces(List<Token> tokens) {
  98. if(tokens == null) {
  99. return null;
  100. }
  101. // for the most part, spaces are superfluous except for one situation(?).
  102. // when they appear between a string literal and '(' they help distinguish
  103. // a function call from another expression form
  104. for(int i = 1; i < (tokens.size() - 1); ++i) {
  105. Token t = tokens.get(i);
  106. if(t.getType() == TokenType.SPACE) {
  107. if((tokens.get(i - 1).getType() == TokenType.STRING) &&
  108. isOp(tokens.get(i + 1), FUNC_START_DELIM)) {
  109. // we want to keep this space
  110. } else {
  111. tokens.remove(i);
  112. --i;
  113. }
  114. }
  115. }
  116. return tokens;
  117. }
  118. private static Expr parseExpression(Type exprType, TokBuf buf,
  119. boolean isSimpleExpr)
  120. {
  121. // FIXME, how do we handle order of ops when no parens?
  122. while(buf.hasNext()) {
  123. Token t = buf.next();
  124. switch(t.getType()) {
  125. case OBJ_NAME:
  126. break;
  127. case LITERAL:
  128. break;
  129. case OP:
  130. break;
  131. case STRING:
  132. WordType wordType = getWordType(t);
  133. if(wordType == null) {
  134. // literal string? or possibly function?
  135. Expr funcExpr = maybeParseFuncCall(t, buf, exprType, isSimpleExpr);
  136. if(funcExpr != null) {
  137. buf.setPendingExpr(funcExpr);
  138. continue;
  139. }
  140. // FIXME
  141. }
  142. break;
  143. case SPACE:
  144. // top-level space is irrelevant
  145. break;
  146. default:
  147. throw new RuntimeException("unknown token type " + t.getType());
  148. }
  149. }
  150. Expr expr = buf.takePendingExpr();
  151. if(expr == null) {
  152. throw new IllegalArgumentException("No expression found?");
  153. }
  154. return expr;
  155. }
  156. private static Expr maybeParseFuncCall(Token firstTok, TokBuf buf,
  157. Type exprType, boolean isSimpleExpr) {
  158. int startPos = buf.curPos();
  159. boolean foundFunc = false;
  160. try {
  161. Token t = buf.peekNext();
  162. if((t == null) || !isOp(t, FUNC_START_DELIM)) {
  163. // not a function call
  164. return null;
  165. }
  166. buf.next();
  167. List<TokBuf> paramBufs = findFuncCallParams(buf);
  168. List<Expr> params = Collections.emptyList();
  169. if(!paramBufs.isEmpty()) {
  170. params = new ArrayList<Expr>(paramBufs.size());
  171. for(TokBuf paramBuf : paramBufs) {
  172. params.add(parseExpression(exprType, paramBuf, isSimpleExpr));
  173. }
  174. }
  175. return new EFunc((String)firstTok.getValue(), params);
  176. } finally {
  177. if(!foundFunc) {
  178. buf.reset(startPos);
  179. }
  180. }
  181. }
  182. private static List<TokBuf> findFuncCallParams(TokBuf buf) {
  183. // simple case, no params
  184. Token t = buf.peekNext();
  185. if((t != null) && isOp(t, FUNC_END_DELIM)) {
  186. buf.next();
  187. return Collections.emptyList();
  188. }
  189. // find closing ")", handle nested parens
  190. List<TokBuf> params = new ArrayList<TokBuf>(3);
  191. int level = 1;
  192. int startPos = buf.curPos();
  193. while(buf.hasNext()) {
  194. t = buf.next();
  195. if(isOp(t, FUNC_START_DELIM)) {
  196. ++level;
  197. } else if(isOp(t, FUNC_END_DELIM)) {
  198. --level;
  199. if(level == 0) {
  200. params.add(buf.subBuf(startPos, buf.prevPos()));
  201. if(params.size() > 1) {
  202. // if there is more than one param and one of them is empty, then
  203. // something is messed up (note, it should not be possible to have
  204. // an empty param if there is only one since we trim superfluous
  205. // spaces)
  206. for(TokBuf paramBuf : params) {
  207. if(!paramBuf.hasNext()) {
  208. throw new IllegalArgumentException(
  209. "Invalid empty parameter for function");
  210. }
  211. }
  212. }
  213. return params;
  214. }
  215. } else if((level == 1) && isOp(t, FUNC_PARAM_SEP)) {
  216. params.add(buf.subBuf(startPos, buf.prevPos()));
  217. startPos = buf.curPos();
  218. }
  219. }
  220. throw new IllegalArgumentException("Missing closing '" + FUNC_END_DELIM +
  221. "' for function call");
  222. }
  223. private static boolean isSimpleExpression(TokBuf buf, Type exprType) {
  224. if(exprType != Type.DEFAULT_VALUE) {
  225. return false;
  226. }
  227. // a leading "=" indicates "full" expression handling for a DEFAULT_VALUE
  228. Token t = buf.peekNext();
  229. if((t != null) && isOp(t, "=")) {
  230. buf.next();
  231. return false;
  232. }
  233. // this is a "simple" DEFAULT_VALUE
  234. return true;
  235. }
  236. private static boolean isOp(Token t, String opStr) {
  237. return ((t.getType() == TokenType.OP) &&
  238. opStr.equalsIgnoreCase((String)t.getValue()));
  239. }
  240. private static WordType getWordType(Token t) {
  241. return WORD_TYPES.get(((String)t.getValue()).toLowerCase());
  242. }
  243. private static void setWordType(WordType type, String... words) {
  244. for(String w : words) {
  245. WORD_TYPES.put(w, type);
  246. }
  247. }
  248. private static final class TokBuf
  249. {
  250. private final List<Token> _tokens;
  251. private final boolean _topLevel;
  252. private int _pos;
  253. private Expr _pendingExpr;
  254. private TokBuf(List<Token> tokens) {
  255. this(tokens, true);
  256. }
  257. private TokBuf(List<Token> tokens, boolean topLevel) {
  258. _tokens = tokens;
  259. _topLevel = topLevel;
  260. }
  261. public boolean isTopLevel() {
  262. return _topLevel;
  263. }
  264. public int curPos() {
  265. return _pos;
  266. }
  267. public int prevPos() {
  268. return _pos - 1;
  269. }
  270. public boolean hasNext() {
  271. return (_pos < _tokens.size());
  272. }
  273. public Token peekNext() {
  274. if(!hasNext()) {
  275. return null;
  276. }
  277. return _tokens.get(_pos);
  278. }
  279. public Token next() {
  280. return _tokens.get(_pos++);
  281. }
  282. public void reset(int pos) {
  283. _pos = pos;
  284. }
  285. public TokBuf subBuf(int start, int end) {
  286. return new TokBuf(_tokens.subList(start, end), false);
  287. }
  288. public void setPendingExpr(Expr expr) {
  289. if(_pendingExpr == null) {
  290. throw new IllegalArgumentException("Found multiple expressions with no operator");
  291. }
  292. _pendingExpr = expr;
  293. }
  294. public Expr takePendingExpr() {
  295. Expr expr = _pendingExpr;
  296. _pendingExpr = null;
  297. return expr;
  298. }
  299. public boolean hasPendingExpr() {
  300. return (_pendingExpr != null);
  301. }
  302. }
  303. public static abstract class Expr
  304. {
  305. public Object evalDefault() {
  306. return eval(null);
  307. }
  308. public boolean evalCondition(RowContext ctx) {
  309. Object val = eval(ctx);
  310. if(val instanceof Boolean) {
  311. return (Boolean)val;
  312. }
  313. // a single value as a conditional expression seems to act like an
  314. // implicit "="
  315. return val.equals(ctx.getThisColumnValue());
  316. }
  317. protected abstract Object eval(RowContext ctx);
  318. }
  319. public interface RowContext
  320. {
  321. public Object getThisColumnValue();
  322. public Object getRowValue(String colName);
  323. }
  324. private static final class ELiteralValue extends Expr
  325. {
  326. private final Object _value;
  327. private ELiteralValue(Object value) {
  328. _value = value;
  329. }
  330. @Override
  331. public Object eval(RowContext ctx) {
  332. return _value;
  333. }
  334. }
  335. private static final class EColumnValue extends Expr
  336. {
  337. private final String _colName;
  338. private EColumnValue(String colName) {
  339. _colName = colName;
  340. }
  341. @Override
  342. public Object eval(RowContext ctx) {
  343. return ctx.getRowValue(_colName);
  344. }
  345. }
  346. private static abstract class EOp
  347. {
  348. }
  349. private static abstract class ECond
  350. {
  351. }
  352. private static class EParen extends Expr
  353. {
  354. private final Expr _expr;
  355. private EParen(Expr expr) {
  356. _expr = expr;
  357. }
  358. @Override
  359. protected Object eval(RowContext ctx) {
  360. return _expr.eval(ctx);
  361. }
  362. }
  363. private static class EFunc extends Expr
  364. {
  365. private final String _name;
  366. private final List<Expr> _params;
  367. private EFunc(String name, List<Expr> params) {
  368. _name = name;
  369. _params = params;
  370. }
  371. @Override
  372. protected Object eval(RowContext ctx) {
  373. // FIXME how do func results act for conditional values?
  374. return false;
  375. }
  376. }
  377. }