Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

DefaultTextFunctions.java 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  1. /*
  2. Copyright (c) 2017 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.impl.expr;
  14. import java.math.BigDecimal;
  15. import com.healthmarketscience.jackcess.expr.EvalContext;
  16. import com.healthmarketscience.jackcess.expr.EvalException;
  17. import com.healthmarketscience.jackcess.expr.Function;
  18. import com.healthmarketscience.jackcess.expr.LocaleContext;
  19. import com.healthmarketscience.jackcess.expr.Value;
  20. import org.apache.commons.lang.WordUtils;
  21. import static com.healthmarketscience.jackcess.impl.expr.DefaultFunctions.*;
  22. import static com.healthmarketscience.jackcess.impl.expr.FunctionSupport.*;
  23. /**
  24. *
  25. * @author James Ahlborn
  26. */
  27. public class DefaultTextFunctions
  28. {
  29. // mask to separate the case conversion value (first two bits) from the char
  30. // conversion value for the StrConv() function
  31. private static final int STR_CONV_MASK = 0x03;
  32. private DefaultTextFunctions() {}
  33. static void init() {
  34. // dummy method to ensure this class is loaded
  35. }
  36. public static final Function ASC = registerFunc(new Func1("Asc") {
  37. @Override
  38. protected Value eval1(EvalContext ctx, Value param1) {
  39. String str = param1.getAsString(ctx);
  40. int len = str.length();
  41. if(len == 0) {
  42. throw new EvalException("No characters in string");
  43. }
  44. int lv = str.charAt(0);
  45. if((lv < 0) || (lv > 255)) {
  46. throw new EvalException("Character code '" + lv +
  47. "' out of range ");
  48. }
  49. return ValueSupport.toValue(lv);
  50. }
  51. });
  52. public static final Function ASCW = registerFunc(new Func1("AscW") {
  53. @Override
  54. protected Value eval1(EvalContext ctx, Value param1) {
  55. String str = param1.getAsString(ctx);
  56. int len = str.length();
  57. if(len == 0) {
  58. throw new EvalException("No characters in string");
  59. }
  60. int lv = str.charAt(0);
  61. return ValueSupport.toValue(lv);
  62. }
  63. });
  64. public static final Function CHR = registerStringFunc(new Func1NullIsNull("Chr") {
  65. @Override
  66. protected Value eval1(EvalContext ctx, Value param1) {
  67. int lv = param1.getAsLongInt(ctx);
  68. if((lv < 0) || (lv > 255)) {
  69. throw new EvalException("Character code '" + lv +
  70. "' out of range ");
  71. }
  72. char[] cs = Character.toChars(lv);
  73. return ValueSupport.toValue(new String(cs));
  74. }
  75. });
  76. public static final Function CHRW = registerStringFunc(new Func1NullIsNull("ChrW") {
  77. @Override
  78. protected Value eval1(EvalContext ctx, Value param1) {
  79. int lv = param1.getAsLongInt(ctx);
  80. char[] cs = Character.toChars(lv);
  81. return ValueSupport.toValue(new String(cs));
  82. }
  83. });
  84. public static final Function STR = registerStringFunc(new Func1NullIsNull("Str") {
  85. @Override
  86. protected Value eval1(EvalContext ctx, Value param1) {
  87. BigDecimal bd = param1.getAsBigDecimal(ctx);
  88. String str = bd.toPlainString();
  89. if(bd.compareTo(BigDecimal.ZERO) >= 0) {
  90. str = " " + str;
  91. }
  92. return ValueSupport.toValue(str);
  93. }
  94. });
  95. public static final Function INSTR = registerFunc(new FuncVar("InStr", 2, 4) {
  96. @Override
  97. protected Value evalVar(EvalContext ctx, Value[] params) {
  98. int idx = 0;
  99. int start = 0;
  100. if(params.length > 2) {
  101. // 1 based offsets
  102. start = params[0].getAsLongInt(ctx) - 1;
  103. ++idx;
  104. }
  105. Value param1 = params[idx++];
  106. if(param1.isNull()) {
  107. return param1;
  108. }
  109. String s1 = param1.getAsString(ctx);
  110. int s1Len = s1.length();
  111. if(s1Len == 0) {
  112. return ValueSupport.ZERO_VAL;
  113. }
  114. Value param2 = params[idx++];
  115. if(param2.isNull()) {
  116. return param2;
  117. }
  118. String s2 = param2.getAsString(ctx);
  119. int s2Len = s2.length();
  120. if(s2Len == 0) {
  121. // 1 based offsets
  122. return ValueSupport.toValue(start + 1);
  123. }
  124. boolean ignoreCase = getIgnoreCase(ctx, params, 3);
  125. int end = s1Len - s2Len;
  126. while(start < end) {
  127. if(s1.regionMatches(ignoreCase, start, s2, 0, s2Len)) {
  128. // 1 based offsets
  129. return ValueSupport.toValue(start + 1);
  130. }
  131. ++start;
  132. }
  133. return ValueSupport.ZERO_VAL;
  134. }
  135. });
  136. public static final Function INSTRREV = registerFunc(new FuncVar("InStrRev", 2, 4) {
  137. @Override
  138. protected Value evalVar(EvalContext ctx, Value[] params) {
  139. Value param1 = params[0];
  140. if(param1.isNull()) {
  141. return param1;
  142. }
  143. String s1 = param1.getAsString(ctx);
  144. int s1Len = s1.length();
  145. if(s1Len == 0) {
  146. return ValueSupport.ZERO_VAL;
  147. }
  148. Value param2 = params[1];
  149. if(param2.isNull()) {
  150. return param2;
  151. }
  152. String s2 = param2.getAsString(ctx);
  153. int s2Len = s2.length();
  154. int start = s1Len - 1;
  155. if(s2Len == 0) {
  156. // 1 based offsets
  157. return ValueSupport.toValue(start + 1);
  158. }
  159. if(params.length > 2) {
  160. start = params[2].getAsLongInt(ctx);
  161. if(start == -1) {
  162. start = s1Len;
  163. }
  164. // 1 based offsets
  165. --start;
  166. }
  167. boolean ignoreCase = getIgnoreCase(ctx, params, 3);
  168. start = Math.min(s1Len - s2Len, start - s2Len + 1);
  169. while(start >= 0) {
  170. if(s1.regionMatches(ignoreCase, start, s2, 0, s2Len)) {
  171. // 1 based offsets
  172. return ValueSupport.toValue(start + 1);
  173. }
  174. --start;
  175. }
  176. return ValueSupport.ZERO_VAL;
  177. }
  178. });
  179. public static final Function LCASE = registerStringFunc(new Func1NullIsNull("LCase") {
  180. @Override
  181. protected Value eval1(EvalContext ctx, Value param1) {
  182. String str = param1.getAsString(ctx);
  183. return ValueSupport.toValue(str.toLowerCase());
  184. }
  185. });
  186. public static final Function UCASE = registerStringFunc(new Func1NullIsNull("UCase") {
  187. @Override
  188. protected Value eval1(EvalContext ctx, Value param1) {
  189. String str = param1.getAsString(ctx);
  190. return ValueSupport.toValue(str.toUpperCase());
  191. }
  192. });
  193. public static final Function LEFT = registerStringFunc(new Func2("Left") {
  194. @Override
  195. protected Value eval2(EvalContext ctx, Value param1, Value param2) {
  196. if(param1.isNull()) {
  197. return param1;
  198. }
  199. String str = param1.getAsString(ctx);
  200. int len = Math.min(str.length(), param2.getAsLongInt(ctx));
  201. return ValueSupport.toValue(str.substring(0, len));
  202. }
  203. });
  204. public static final Function RIGHT = registerStringFunc(new Func2("Right") {
  205. @Override
  206. protected Value eval2(EvalContext ctx, Value param1, Value param2) {
  207. if(param1.isNull()) {
  208. return param1;
  209. }
  210. String str = param1.getAsString(ctx);
  211. int strLen = str.length();
  212. int len = Math.min(strLen, param2.getAsLongInt(ctx));
  213. return ValueSupport.toValue(str.substring(strLen - len, strLen));
  214. }
  215. });
  216. public static final Function MID = registerStringFunc(new FuncVar("Mid", 2, 3) {
  217. @Override
  218. protected Value evalVar(EvalContext ctx, Value[] params) {
  219. Value param1 = params[0];
  220. if(param1.isNull()) {
  221. return param1;
  222. }
  223. String str = param1.getAsString(ctx);
  224. int strLen = str.length();
  225. // 1 based offsets
  226. int start = Math.min(strLen, params[1].getAsLongInt(ctx) - 1);
  227. int len = Math.min(
  228. ((params.length > 2) ? params[2].getAsLongInt(ctx) : strLen),
  229. (strLen - start));
  230. return ValueSupport.toValue(str.substring(start, start + len));
  231. }
  232. });
  233. public static final Function LEN = registerFunc(new Func1NullIsNull("Len") {
  234. @Override
  235. protected Value eval1(EvalContext ctx, Value param1) {
  236. String str = param1.getAsString(ctx);
  237. return ValueSupport.toValue(str.length());
  238. }
  239. });
  240. public static final Function LTRIM = registerStringFunc(new Func1NullIsNull("LTrim") {
  241. @Override
  242. protected Value eval1(EvalContext ctx, Value param1) {
  243. String str = param1.getAsString(ctx);
  244. return ValueSupport.toValue(trim(str, true, false));
  245. }
  246. });
  247. public static final Function RTRIM = registerStringFunc(new Func1NullIsNull("RTrim") {
  248. @Override
  249. protected Value eval1(EvalContext ctx, Value param1) {
  250. String str = param1.getAsString(ctx);
  251. return ValueSupport.toValue(trim(str, false, true));
  252. }
  253. });
  254. public static final Function TRIM = registerStringFunc(new Func1NullIsNull("Trim") {
  255. @Override
  256. protected Value eval1(EvalContext ctx, Value param1) {
  257. String str = param1.getAsString(ctx);
  258. return ValueSupport.toValue(trim(str, true, true));
  259. }
  260. });
  261. public static final Function REPLACE = registerStringFunc(new FuncVar("Replace", 3, 6) {
  262. @Override
  263. protected Value evalVar(EvalContext ctx, Value[] params) {
  264. String str = params[0].getAsString(ctx);
  265. String searchStr = params[1].getAsString(ctx);
  266. String replStr = params[2].getAsString(ctx);
  267. int strLen = str.length();
  268. int start = getOptionalIntParam(ctx, params, 3, 1) - 1;
  269. int count = getOptionalIntParam(ctx, params, 4, -1);
  270. boolean ignoreCase = getIgnoreCase(ctx, params, 5);
  271. if(start >= strLen) {
  272. return ValueSupport.EMPTY_STR_VAL;
  273. }
  274. int searchLen = searchStr.length();
  275. if((searchLen == 0) || (count == 0)) {
  276. String result = str;
  277. if(start > 0) {
  278. result = str.substring(start);
  279. }
  280. return ValueSupport.toValue(result);
  281. }
  282. if(count < 0) {
  283. count = strLen;
  284. }
  285. StringBuilder result = new StringBuilder(strLen);
  286. int matchCount = 0;
  287. for(int i = start; i < strLen; ++i) {
  288. if((matchCount < count) &&
  289. str.regionMatches(ignoreCase, i, searchStr, 0, searchLen)) {
  290. result.append(replStr);
  291. ++matchCount;
  292. i += searchLen - 1;
  293. } else {
  294. result.append(str.charAt(i));
  295. }
  296. }
  297. return ValueSupport.toValue(result.toString());
  298. }
  299. });
  300. public static final Function SPACE = registerStringFunc(new Func1("Space") {
  301. @Override
  302. protected Value eval1(EvalContext ctx, Value param1) {
  303. int lv = param1.getAsLongInt(ctx);
  304. return ValueSupport.toValue(nchars(lv, ' '));
  305. }
  306. });
  307. public static final Function STRCOMP = registerFunc(new FuncVar("StrComp", 2, 3) {
  308. @Override
  309. protected Value evalVar(EvalContext ctx, Value[] params) {
  310. Value param1 = params[0];
  311. Value param2 = params[1];
  312. if(param1.isNull() || param2.isNull()) {
  313. return ValueSupport.NULL_VAL;
  314. }
  315. String s1 = param1.getAsString(ctx);
  316. String s2 = param2.getAsString(ctx);
  317. boolean ignoreCase = getIgnoreCase(ctx, params, 2);
  318. int cmp = (ignoreCase ?
  319. s1.compareToIgnoreCase(s2) : s1.compareTo(s2));
  320. // stupid java doesn't return 1, -1, 0...
  321. return ((cmp < 0) ? ValueSupport.NEG_ONE_VAL :
  322. ((cmp > 0) ? ValueSupport.ONE_VAL :
  323. ValueSupport.ZERO_VAL));
  324. }
  325. });
  326. public static final Function STRCONV = registerStringFunc(new FuncVar("StrConv", 2, 3) {
  327. @Override
  328. protected Value evalVar(EvalContext ctx, Value[] params) {
  329. Value param1 = params[0];
  330. if(param1.isNull()) {
  331. return ValueSupport.NULL_VAL;
  332. }
  333. String str = param1.getAsString(ctx);
  334. int conversion = params[1].getAsLongInt(ctx);
  335. // TODO, for now, ignore locale id...?
  336. // int localeId = params[2];
  337. int caseConv = STR_CONV_MASK & conversion;
  338. int charConv = (~STR_CONV_MASK) & conversion;
  339. switch(caseConv) {
  340. case 1:
  341. // vbUpperCase
  342. str = str.toUpperCase();
  343. break;
  344. case 2:
  345. // vbLowerCase
  346. str = str.toLowerCase();
  347. break;
  348. case 3:
  349. // vbProperCase
  350. str = WordUtils.capitalize(str.toLowerCase());
  351. break;
  352. default:
  353. // do nothing
  354. }
  355. if(charConv != 0) {
  356. // 64 = vbUnicode, all java strings are already unicode,so nothing to do
  357. if(charConv != 64) {
  358. throw new EvalException("Unsupported character conversion " + charConv);
  359. }
  360. }
  361. return ValueSupport.toValue(str);
  362. }
  363. });
  364. public static final Function STRING = registerStringFunc(new Func2("String") {
  365. @Override
  366. protected Value eval2(EvalContext ctx, Value param1, Value param2) {
  367. if(param1.isNull() || param2.isNull()) {
  368. return ValueSupport.NULL_VAL;
  369. }
  370. int lv = param1.getAsLongInt(ctx);
  371. char c = (char)(param2.getAsString(ctx).charAt(0) % 256);
  372. return ValueSupport.toValue(nchars(lv, c));
  373. }
  374. });
  375. public static final Function STRREVERSE = registerFunc(new Func1("StrReverse") {
  376. @Override
  377. protected Value eval1(EvalContext ctx, Value param1) {
  378. String str = param1.getAsString(ctx);
  379. return ValueSupport.toValue(
  380. new StringBuilder(str).reverse().toString());
  381. }
  382. });
  383. public static final Function FORMAT = registerStringFunc(new FuncVar("Format", 1, 4) {
  384. @Override
  385. protected Value evalVar(EvalContext ctx, Value[] params) {
  386. Value expr = params[0];
  387. if(params.length < 2) {
  388. // no formatting, do simple string conversion
  389. if(expr.isNull()) {
  390. return ValueSupport.NULL_VAL;
  391. }
  392. return ValueSupport.toValue(expr.getAsString(ctx));
  393. }
  394. String fmtStr = params[1].getAsString(ctx);
  395. int firstDay = DefaultDateFunctions.getFirstDayParam(ctx, params, 2);
  396. int firstWeekType = DefaultDateFunctions.getFirstWeekTypeParam(ctx, params, 3);
  397. return FormatUtil.format(ctx, expr, fmtStr, firstDay, firstWeekType);
  398. }
  399. });
  400. private static String nchars(int num, char c) {
  401. StringBuilder sb = new StringBuilder(num);
  402. nchars(sb, num, c);
  403. return sb.toString();
  404. }
  405. static void nchars(StringBuilder sb, int num, char c) {
  406. for(int i = 0; i < num; ++i) {
  407. sb.append(c);
  408. }
  409. }
  410. private static String trim(String str, boolean doLeft, boolean doRight) {
  411. int start = 0;
  412. int end = str.length();
  413. if(doLeft) {
  414. while((start < end) && (str.charAt(start) == ' ')) {
  415. ++start;
  416. }
  417. }
  418. if(doRight) {
  419. while((start < end) && (str.charAt(end - 1) == ' ')) {
  420. --end;
  421. }
  422. }
  423. return str.substring(start, end);
  424. }
  425. private static boolean getIgnoreCase(EvalContext ctx, Value[] params, int idx) {
  426. boolean ignoreCase = true;
  427. if(params.length > idx) {
  428. ignoreCase = doIgnoreCase(ctx, params[idx]);
  429. }
  430. return ignoreCase;
  431. }
  432. private static boolean doIgnoreCase(LocaleContext ctx, Value paramCmp) {
  433. int cmpType = paramCmp.getAsLongInt(ctx);
  434. switch(cmpType) {
  435. case -1:
  436. // vbUseCompareOption -> default is binary
  437. case 0:
  438. // vbBinaryCompare
  439. return false;
  440. case 1:
  441. // vbTextCompare
  442. return true;
  443. default:
  444. // vbDatabaseCompare -> unsupported
  445. throw new EvalException("Unsupported compare type " + cmpType);
  446. }
  447. }
  448. }