1. /*
  2. * Copyright 1999-2004 The Apache Software Foundation
  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. package org.apache.commons.jxpath.ri.compiler;
  17. import java.text.DecimalFormat;
  18. import java.text.DecimalFormatSymbols;
  19. import java.text.NumberFormat;
  20. import java.util.Collection;
  21. import java.util.Locale;
  22. import org.apache.commons.jxpath.JXPathContext;
  23. import org.apache.commons.jxpath.JXPathException;
  24. import org.apache.commons.jxpath.ri.Compiler;
  25. import org.apache.commons.jxpath.ri.EvalContext;
  26. import org.apache.commons.jxpath.ri.InfoSetUtil;
  27. import org.apache.commons.jxpath.ri.model.NodePointer;
  28. /**
  29. * An element of the compile tree representing one of built-in functions
  30. * like "position()" or "number()".
  31. *
  32. * @author Dmitri Plotnikov
  33. * @version $Revision: 1.16 $ $Date: 2004/04/01 02:53:45 $
  34. */
  35. public class CoreFunction extends Operation {
  36. private static final Double ZERO = new Double(0);
  37. private int functionCode;
  38. public CoreFunction(int functionCode, Expression args[]) {
  39. super(args);
  40. this.functionCode = functionCode;
  41. }
  42. public int getFunctionCode() {
  43. return functionCode;
  44. }
  45. protected String getFunctionName() {
  46. switch (functionCode) {
  47. case Compiler.FUNCTION_LAST :
  48. return "last";
  49. case Compiler.FUNCTION_POSITION :
  50. return "position";
  51. case Compiler.FUNCTION_COUNT :
  52. return "count";
  53. case Compiler.FUNCTION_ID :
  54. return "id";
  55. case Compiler.FUNCTION_LOCAL_NAME :
  56. return "local-name";
  57. case Compiler.FUNCTION_NAMESPACE_URI :
  58. return "namespace-uri";
  59. case Compiler.FUNCTION_NAME :
  60. return "name";
  61. case Compiler.FUNCTION_STRING :
  62. return "string";
  63. case Compiler.FUNCTION_CONCAT :
  64. return "concat";
  65. case Compiler.FUNCTION_STARTS_WITH :
  66. return "starts-with";
  67. case Compiler.FUNCTION_CONTAINS :
  68. return "contains";
  69. case Compiler.FUNCTION_SUBSTRING_BEFORE :
  70. return "substring-before";
  71. case Compiler.FUNCTION_SUBSTRING_AFTER :
  72. return "substring-after";
  73. case Compiler.FUNCTION_SUBSTRING :
  74. return "substring";
  75. case Compiler.FUNCTION_STRING_LENGTH :
  76. return "string-length";
  77. case Compiler.FUNCTION_NORMALIZE_SPACE :
  78. return "normalize-space";
  79. case Compiler.FUNCTION_TRANSLATE :
  80. return "translate";
  81. case Compiler.FUNCTION_BOOLEAN :
  82. return "boolean";
  83. case Compiler.FUNCTION_NOT :
  84. return "not";
  85. case Compiler.FUNCTION_TRUE :
  86. return "true";
  87. case Compiler.FUNCTION_FALSE :
  88. return "false";
  89. case Compiler.FUNCTION_LANG :
  90. return "lang";
  91. case Compiler.FUNCTION_NUMBER :
  92. return "number";
  93. case Compiler.FUNCTION_SUM :
  94. return "sum";
  95. case Compiler.FUNCTION_FLOOR :
  96. return "floor";
  97. case Compiler.FUNCTION_CEILING :
  98. return "ceiling";
  99. case Compiler.FUNCTION_ROUND :
  100. return "round";
  101. case Compiler.FUNCTION_KEY :
  102. return "key";
  103. case Compiler.FUNCTION_FORMAT_NUMBER:
  104. return "format-number";
  105. }
  106. return "unknownFunction" + functionCode + "()";
  107. }
  108. public Expression getArg1() {
  109. return args[0];
  110. }
  111. public Expression getArg2() {
  112. return args[1];
  113. }
  114. public Expression getArg3() {
  115. return args[2];
  116. }
  117. public int getArgumentCount() {
  118. if (args == null) {
  119. return 0;
  120. }
  121. return args.length;
  122. }
  123. /**
  124. * Returns true if any argument is context dependent or if
  125. * the function is last(), position(), boolean(), local-name(),
  126. * name(), string(), lang(), number().
  127. */
  128. public boolean computeContextDependent() {
  129. if (super.computeContextDependent()) {
  130. return true;
  131. }
  132. switch(functionCode) {
  133. case Compiler.FUNCTION_LAST:
  134. case Compiler.FUNCTION_POSITION:
  135. return true;
  136. case Compiler.FUNCTION_BOOLEAN:
  137. case Compiler.FUNCTION_LOCAL_NAME:
  138. case Compiler.FUNCTION_NAME:
  139. case Compiler.FUNCTION_NAMESPACE_URI:
  140. case Compiler.FUNCTION_STRING:
  141. case Compiler.FUNCTION_LANG:
  142. case Compiler.FUNCTION_NUMBER:
  143. return args == null || args.length == 0;
  144. case Compiler.FUNCTION_COUNT:
  145. case Compiler.FUNCTION_ID:
  146. case Compiler.FUNCTION_CONCAT:
  147. case Compiler.FUNCTION_STARTS_WITH:
  148. case Compiler.FUNCTION_CONTAINS:
  149. case Compiler.FUNCTION_SUBSTRING_BEFORE:
  150. case Compiler.FUNCTION_SUBSTRING_AFTER:
  151. case Compiler.FUNCTION_SUBSTRING:
  152. case Compiler.FUNCTION_STRING_LENGTH:
  153. case Compiler.FUNCTION_NORMALIZE_SPACE:
  154. case Compiler.FUNCTION_TRANSLATE:
  155. case Compiler.FUNCTION_NOT:
  156. case Compiler.FUNCTION_TRUE:
  157. case Compiler.FUNCTION_FALSE:
  158. case Compiler.FUNCTION_SUM:
  159. case Compiler.FUNCTION_FLOOR:
  160. case Compiler.FUNCTION_CEILING:
  161. case Compiler.FUNCTION_ROUND:
  162. return false;
  163. case Compiler.FUNCTION_FORMAT_NUMBER:
  164. return args != null && args.length == 2;
  165. }
  166. return false;
  167. }
  168. public String toString() {
  169. StringBuffer buffer = new StringBuffer();
  170. buffer.append(getFunctionName());
  171. buffer.append('(');
  172. Expression args[] = getArguments();
  173. if (args != null) {
  174. for (int i = 0; i < args.length; i++) {
  175. if (i > 0) {
  176. buffer.append(", ");
  177. }
  178. buffer.append(args[i]);
  179. }
  180. }
  181. buffer.append(')');
  182. return buffer.toString();
  183. }
  184. public Object compute(EvalContext context) {
  185. return computeValue(context);
  186. }
  187. /**
  188. * Computes a built-in function
  189. */
  190. public Object computeValue(EvalContext context) {
  191. switch (functionCode) {
  192. case Compiler.FUNCTION_LAST :
  193. return functionLast(context);
  194. case Compiler.FUNCTION_POSITION :
  195. return functionPosition(context);
  196. case Compiler.FUNCTION_COUNT :
  197. return functionCount(context);
  198. case Compiler.FUNCTION_LANG :
  199. return functionLang(context);
  200. case Compiler.FUNCTION_ID :
  201. return functionID(context);
  202. case Compiler.FUNCTION_LOCAL_NAME :
  203. return functionLocalName(context);
  204. case Compiler.FUNCTION_NAMESPACE_URI :
  205. return functionNamespaceURI(context);
  206. case Compiler.FUNCTION_NAME :
  207. return functionName(context);
  208. case Compiler.FUNCTION_STRING :
  209. return functionString(context);
  210. case Compiler.FUNCTION_CONCAT :
  211. return functionConcat(context);
  212. case Compiler.FUNCTION_STARTS_WITH :
  213. return functionStartsWith(context);
  214. case Compiler.FUNCTION_CONTAINS :
  215. return functionContains(context);
  216. case Compiler.FUNCTION_SUBSTRING_BEFORE :
  217. return functionSubstringBefore(context);
  218. case Compiler.FUNCTION_SUBSTRING_AFTER :
  219. return functionSubstringAfter(context);
  220. case Compiler.FUNCTION_SUBSTRING :
  221. return functionSubstring(context);
  222. case Compiler.FUNCTION_STRING_LENGTH :
  223. return functionStringLength(context);
  224. case Compiler.FUNCTION_NORMALIZE_SPACE :
  225. return functionNormalizeSpace(context);
  226. case Compiler.FUNCTION_TRANSLATE :
  227. return functionTranslate(context);
  228. case Compiler.FUNCTION_BOOLEAN :
  229. return functionBoolean(context);
  230. case Compiler.FUNCTION_NOT :
  231. return functionNot(context);
  232. case Compiler.FUNCTION_TRUE :
  233. return functionTrue(context);
  234. case Compiler.FUNCTION_FALSE :
  235. return functionFalse(context);
  236. case Compiler.FUNCTION_NULL :
  237. return functionNull(context);
  238. case Compiler.FUNCTION_NUMBER :
  239. return functionNumber(context);
  240. case Compiler.FUNCTION_SUM :
  241. return functionSum(context);
  242. case Compiler.FUNCTION_FLOOR :
  243. return functionFloor(context);
  244. case Compiler.FUNCTION_CEILING :
  245. return functionCeiling(context);
  246. case Compiler.FUNCTION_ROUND :
  247. return functionRound(context);
  248. case Compiler.FUNCTION_KEY :
  249. return functionKey(context);
  250. case Compiler.FUNCTION_FORMAT_NUMBER :
  251. return functionFormatNumber(context);
  252. }
  253. return null;
  254. }
  255. protected Object functionLast(EvalContext context) {
  256. assertArgCount(0);
  257. // Move the position to the beginning and iterate through
  258. // the context to count nodes.
  259. int old = context.getCurrentPosition();
  260. context.reset();
  261. int count = 0;
  262. while (context.nextNode()) {
  263. count++;
  264. }
  265. // Restore the current position.
  266. if (old != 0) {
  267. context.setPosition(old);
  268. }
  269. return new Double(count);
  270. }
  271. protected Object functionPosition(EvalContext context) {
  272. assertArgCount(0);
  273. return new Integer(context.getCurrentPosition());
  274. }
  275. protected Object functionCount(EvalContext context) {
  276. assertArgCount(1);
  277. Expression arg1 = getArg1();
  278. int count = 0;
  279. Object value = arg1.compute(context);
  280. if (value instanceof NodePointer) {
  281. value = ((NodePointer) value).getValue();
  282. }
  283. if (value instanceof EvalContext) {
  284. EvalContext ctx = (EvalContext) value;
  285. while (ctx.hasNext()) {
  286. ctx.next();
  287. count++;
  288. }
  289. }
  290. else if (value instanceof Collection) {
  291. count = ((Collection) value).size();
  292. }
  293. else if (value == null) {
  294. count = 0;
  295. }
  296. else {
  297. count = 1;
  298. }
  299. return new Double(count);
  300. }
  301. protected Object functionLang(EvalContext context) {
  302. assertArgCount(1);
  303. String lang = InfoSetUtil.stringValue(getArg1().computeValue(context));
  304. NodePointer pointer = (NodePointer) context.getSingleNodePointer();
  305. if (pointer == null) {
  306. return Boolean.FALSE;
  307. }
  308. return pointer.isLanguage(lang) ? Boolean.TRUE : Boolean.FALSE;
  309. }
  310. protected Object functionID(EvalContext context) {
  311. assertArgCount(1);
  312. String id = InfoSetUtil.stringValue(getArg1().computeValue(context));
  313. JXPathContext jxpathContext = context.getJXPathContext();
  314. NodePointer pointer = (NodePointer) jxpathContext.getContextPointer();
  315. return pointer.getPointerByID(jxpathContext, id);
  316. }
  317. protected Object functionKey(EvalContext context) {
  318. assertArgCount(2);
  319. String key = InfoSetUtil.stringValue(getArg1().computeValue(context));
  320. String value = InfoSetUtil.stringValue(getArg2().computeValue(context));
  321. JXPathContext jxpathContext = context.getJXPathContext();
  322. NodePointer pointer = (NodePointer) jxpathContext.getContextPointer();
  323. return pointer.getPointerByKey(jxpathContext, key, value);
  324. }
  325. protected Object functionNamespaceURI(EvalContext context) {
  326. if (getArgumentCount() == 0) {
  327. NodePointer ptr = context.getCurrentNodePointer();
  328. String str = ptr.getNamespaceURI();
  329. return str == null ? "" : str;
  330. }
  331. assertArgCount(1);
  332. Object set = getArg1().compute(context);
  333. if (set instanceof EvalContext) {
  334. EvalContext ctx = (EvalContext) set;
  335. if (ctx.hasNext()) {
  336. NodePointer ptr = (NodePointer) ctx.next();
  337. String str = ptr.getNamespaceURI();
  338. return str == null ? "" : str;
  339. }
  340. }
  341. return "";
  342. }
  343. protected Object functionLocalName(EvalContext context) {
  344. if (getArgumentCount() == 0) {
  345. NodePointer ptr = context.getCurrentNodePointer();
  346. return ptr.getName().getName();
  347. }
  348. assertArgCount(1);
  349. Object set = getArg1().compute(context);
  350. if (set instanceof EvalContext) {
  351. EvalContext ctx = (EvalContext) set;
  352. if (ctx.hasNext()) {
  353. NodePointer ptr = (NodePointer) ctx.next();
  354. return ptr.getName().getName();
  355. }
  356. }
  357. return "";
  358. }
  359. protected Object functionName(EvalContext context) {
  360. if (getArgumentCount() == 0) {
  361. NodePointer ptr = context.getCurrentNodePointer();
  362. return ptr.getName().toString();
  363. }
  364. assertArgCount(1);
  365. Object set = getArg1().compute(context);
  366. if (set instanceof EvalContext) {
  367. EvalContext ctx = (EvalContext) set;
  368. if (ctx.hasNext()) {
  369. NodePointer ptr = (NodePointer) ctx.next();
  370. return ptr.getName().toString();
  371. }
  372. }
  373. return "";
  374. }
  375. protected Object functionString(EvalContext context) {
  376. if (getArgumentCount() == 0) {
  377. return InfoSetUtil.stringValue(context.getCurrentNodePointer());
  378. }
  379. assertArgCount(1);
  380. return InfoSetUtil.stringValue(getArg1().computeValue(context));
  381. }
  382. protected Object functionConcat(EvalContext context) {
  383. if (getArgumentCount() < 2) {
  384. assertArgCount(2);
  385. }
  386. StringBuffer buffer = new StringBuffer();
  387. Expression args[] = getArguments();
  388. for (int i = 0; i < args.length; i++) {
  389. buffer.append(InfoSetUtil.stringValue(args[i].compute(context)));
  390. }
  391. return buffer.toString();
  392. }
  393. protected Object functionStartsWith(EvalContext context) {
  394. assertArgCount(2);
  395. String s1 = InfoSetUtil.stringValue(getArg1().computeValue(context));
  396. String s2 = InfoSetUtil.stringValue(getArg2().computeValue(context));
  397. return s1.startsWith(s2) ? Boolean.TRUE : Boolean.FALSE;
  398. }
  399. protected Object functionContains(EvalContext context) {
  400. assertArgCount(2);
  401. String s1 = InfoSetUtil.stringValue(getArg1().computeValue(context));
  402. String s2 = InfoSetUtil.stringValue(getArg2().computeValue(context));
  403. return s1.indexOf(s2) != -1 ? Boolean.TRUE : Boolean.FALSE;
  404. }
  405. protected Object functionSubstringBefore(EvalContext context) {
  406. assertArgCount(2);
  407. String s1 = InfoSetUtil.stringValue(getArg1().computeValue(context));
  408. String s2 = InfoSetUtil.stringValue(getArg2().computeValue(context));
  409. int index = s1.indexOf(s2);
  410. if (index == -1) {
  411. return "";
  412. }
  413. return s1.substring(0, index);
  414. }
  415. protected Object functionSubstringAfter(EvalContext context) {
  416. assertArgCount(2);
  417. String s1 = InfoSetUtil.stringValue(getArg1().computeValue(context));
  418. String s2 = InfoSetUtil.stringValue(getArg2().computeValue(context));
  419. int index = s1.indexOf(s2);
  420. if (index == -1) {
  421. return "";
  422. }
  423. return s1.substring(index + s2.length());
  424. }
  425. protected Object functionSubstring(EvalContext context) {
  426. int ac = getArgumentCount();
  427. if (ac != 2 && ac != 3) {
  428. assertArgCount(2);
  429. }
  430. String s1 = InfoSetUtil.stringValue(getArg1().computeValue(context));
  431. double from = InfoSetUtil.doubleValue(getArg2().computeValue(context));
  432. if (Double.isNaN(from)) {
  433. return "";
  434. }
  435. from = Math.round(from);
  436. if (ac == 2) {
  437. if (from < 1) {
  438. from = 1;
  439. }
  440. return s1.substring((int) from - 1);
  441. }
  442. else {
  443. double length =
  444. InfoSetUtil.doubleValue(getArg3().computeValue(context));
  445. length = Math.round(length);
  446. if (length < 0) {
  447. return "";
  448. }
  449. double to = from + length;
  450. if (to < 1) {
  451. return "";
  452. }
  453. if (to > s1.length() + 1) {
  454. if (from < 1) {
  455. from = 1;
  456. }
  457. return s1.substring((int) from - 1);
  458. }
  459. if (from < 1) {
  460. from = 1;
  461. }
  462. return s1.substring((int) from - 1, (int) (to - 1));
  463. }
  464. }
  465. protected Object functionStringLength(EvalContext context) {
  466. String s;
  467. if (getArgumentCount() == 0) {
  468. s = InfoSetUtil.stringValue(context.getCurrentNodePointer());
  469. }
  470. else {
  471. assertArgCount(1);
  472. s = InfoSetUtil.stringValue(getArg1().computeValue(context));
  473. }
  474. return new Double(s.length());
  475. }
  476. protected Object functionNormalizeSpace(EvalContext context) {
  477. assertArgCount(1);
  478. String s = InfoSetUtil.stringValue(getArg1().computeValue(context));
  479. char chars[] = s.toCharArray();
  480. int out = 0;
  481. int phase = 0;
  482. for (int in = 0; in < chars.length; in++) {
  483. switch(chars[in]) {
  484. case 0x20:
  485. case 0x9:
  486. case 0xD:
  487. case 0xA:
  488. if (phase == 0) { // beginning
  489. ;
  490. }
  491. else if (phase == 1) { // non-space
  492. phase = 2;
  493. chars[out++] = ' ';
  494. }
  495. break;
  496. default:
  497. chars[out++] = chars[in];
  498. phase = 1;
  499. }
  500. }
  501. if (phase == 2) { // trailing-space
  502. out--;
  503. }
  504. return new String(chars, 0, out);
  505. }
  506. protected Object functionTranslate(EvalContext context) {
  507. assertArgCount(3);
  508. String s1 = InfoSetUtil.stringValue(getArg1().computeValue(context));
  509. String s2 = InfoSetUtil.stringValue(getArg2().computeValue(context));
  510. String s3 = InfoSetUtil.stringValue(getArg3().computeValue(context));
  511. char chars[] = s1.toCharArray();
  512. int out = 0;
  513. for (int in = 0; in < chars.length; in++) {
  514. char c = chars[in];
  515. int inx = s2.indexOf(c);
  516. if (inx != -1) {
  517. if (inx < s3.length()) {
  518. chars[out++] = s3.charAt(inx);
  519. }
  520. }
  521. else {
  522. chars[out++] = c;
  523. }
  524. }
  525. return new String(chars, 0, out);
  526. }
  527. protected Object functionBoolean(EvalContext context) {
  528. assertArgCount(1);
  529. return InfoSetUtil.booleanValue(getArg1().computeValue(context))
  530. ? Boolean.TRUE
  531. : Boolean.FALSE;
  532. }
  533. protected Object functionNot(EvalContext context) {
  534. assertArgCount(1);
  535. return InfoSetUtil.booleanValue(getArg1().computeValue(context))
  536. ? Boolean.FALSE
  537. : Boolean.TRUE;
  538. }
  539. protected Object functionTrue(EvalContext context) {
  540. assertArgCount(0);
  541. return Boolean.TRUE;
  542. }
  543. protected Object functionFalse(EvalContext context) {
  544. assertArgCount(0);
  545. return Boolean.FALSE;
  546. }
  547. protected Object functionNull(EvalContext context) {
  548. assertArgCount(0);
  549. return null;
  550. }
  551. protected Object functionNumber(EvalContext context) {
  552. if (getArgumentCount() == 0) {
  553. return InfoSetUtil.number(context.getCurrentNodePointer());
  554. }
  555. assertArgCount(1);
  556. return InfoSetUtil.number(getArg1().computeValue(context));
  557. }
  558. protected Object functionSum(EvalContext context) {
  559. assertArgCount(1);
  560. Object v = getArg1().compute(context);
  561. if (v == null) {
  562. return ZERO;
  563. }
  564. else if (v instanceof EvalContext) {
  565. double sum = 0.0;
  566. EvalContext ctx = (EvalContext) v;
  567. while (ctx.hasNext()) {
  568. NodePointer ptr = (NodePointer) ctx.next();
  569. sum += InfoSetUtil.doubleValue(ptr);
  570. }
  571. return new Double(sum);
  572. }
  573. throw new JXPathException(
  574. "Invalid argument type for 'sum': " + v.getClass().getName());
  575. }
  576. protected Object functionFloor(EvalContext context) {
  577. assertArgCount(1);
  578. double v = InfoSetUtil.doubleValue(getArg1().computeValue(context));
  579. return new Double(Math.floor(v));
  580. }
  581. protected Object functionCeiling(EvalContext context) {
  582. assertArgCount(1);
  583. double v = InfoSetUtil.doubleValue(getArg1().computeValue(context));
  584. return new Double(Math.ceil(v));
  585. }
  586. protected Object functionRound(EvalContext context) {
  587. assertArgCount(1);
  588. double v = InfoSetUtil.doubleValue(getArg1().computeValue(context));
  589. return new Double(Math.round(v));
  590. }
  591. private Object functionFormatNumber(EvalContext context) {
  592. int ac = getArgumentCount();
  593. if (ac != 2 && ac != 3) {
  594. assertArgCount(2);
  595. }
  596. double number =
  597. InfoSetUtil.doubleValue(getArg1().computeValue(context));
  598. String pattern =
  599. InfoSetUtil.stringValue(getArg2().computeValue(context));
  600. DecimalFormatSymbols symbols = null;
  601. if (ac == 3) {
  602. String symbolsName =
  603. InfoSetUtil.stringValue(getArg3().computeValue(context));
  604. symbols =
  605. context.getJXPathContext().getDecimalFormatSymbols(symbolsName);
  606. }
  607. else {
  608. NodePointer pointer = context.getCurrentNodePointer();
  609. Locale locale;
  610. if (pointer != null) {
  611. locale = pointer.getLocale();
  612. }
  613. else {
  614. locale = context.getJXPathContext().getLocale();
  615. }
  616. symbols = new DecimalFormatSymbols(locale);
  617. }
  618. DecimalFormat format = (DecimalFormat) NumberFormat.getInstance();
  619. format.setDecimalFormatSymbols(symbols);
  620. format.applyLocalizedPattern(pattern);
  621. return format.format(number);
  622. }
  623. private void assertArgCount(int count) {
  624. if (getArgumentCount() != count) {
  625. throw new JXPathException("Incorrect number of argument: " + this);
  626. }
  627. }
  628. }