fork download
  1. package au.com.cellmaster.gwt.server;
  2.  
  3.  
  4. import java.util.ArrayList;
  5. import java.util.Stack;
  6.  
  7. public class ParseFormula {
  8.  
  9. private String formula;
  10. private int offset = 0;
  11.  
  12. public ParseFormula(String formula) {
  13.  
  14. this.formula = formula;
  15.  
  16. }
  17.  
  18. static public final String TOK_TYPE_NOOP = "noop";
  19. static public final String TOK_TYPE_OPERAND = "operand";
  20. static public final String TOK_TYPE_FUNCTION = "function";
  21. static public final String TOK_TYPE_SUBEXPR = "subexpression";
  22. static public final String TOK_TYPE_ARGUMENT = "argument";
  23. static public final String TOK_TYPE_OP_PRE = "operator-prefix";
  24. static public final String TOK_TYPE_OP_IN = "operator-infix";
  25. static public final String TOK_TYPE_OP_POST = "operator-postfix";
  26. static public final String TOK_TYPE_WSPACE = "white-space";
  27. static public final String TOK_TYPE_UNKNOWN = "unknown";
  28.  
  29. static public final String TOK_SUBTYPE_START = "start";
  30. static public final String TOK_SUBTYPE_STOP = "stop";
  31.  
  32. static public final String TOK_SUBTYPE_TEXT = "text";
  33. static public final String TOK_SUBTYPE_NUMBER = "number";
  34. static public final String TOK_SUBTYPE_LOGICAL = "logical";
  35. static public final String TOK_SUBTYPE_ERROR = "error";
  36. static public final String TOK_SUBTYPE_RANGE = "range";
  37.  
  38. static public final String TOK_SUBTYPE_MATH = "math";
  39. static public final String TOK_SUBTYPE_CONCAT = "concatenate";
  40. static public final String TOK_SUBTYPE_INTERSECT = "intersect";
  41. static public final String TOK_SUBTYPE_UNION = "union";
  42.  
  43.  
  44. public class FormulaToken {
  45. public FormulaToken(String value, String type, String subtype) {
  46. this.value = value;
  47. this.type = type;
  48. this.subtype = subtype;
  49. }
  50. String value;
  51. String type;
  52. String subtype;
  53. }
  54.  
  55. public class TokenList {
  56. public TokenList() {
  57.  
  58. this.items = new ArrayList<FormulaToken>();
  59.  
  60. this.index = -1;
  61. }
  62. public int index;
  63. public ArrayList<FormulaToken> items;
  64.  
  65. public FormulaToken add(String value, String type) {
  66. return add( value, type, null);
  67. }
  68. public FormulaToken add(String value, String type, String subtype) {
  69. if (null == subtype) subtype = "";
  70. FormulaToken token = new FormulaToken(value, type, subtype);
  71. this.addRef(token);
  72. return token;
  73. }
  74.  
  75. public void addRef(FormulaToken token) {
  76. this.items.add(token);
  77. }
  78.  
  79. public void reset() {
  80. this.index = -1;
  81. }
  82.  
  83. public boolean BOF() {
  84. return (this.index <= 0);
  85. }
  86.  
  87.  
  88. public boolean EOF() {
  89. return (this.index >= (this.items.size() - 1));
  90. }
  91.  
  92. public boolean moveNext() {
  93. if (this.EOF()) return false;
  94. this.index++; return true;
  95. }
  96.  
  97. public FormulaToken current() {
  98. if (this.index == -1) return null;
  99. return (this.items.get(this.index));
  100. }
  101. public FormulaToken next() {
  102. if (this.EOF()) return null;
  103. return (this.items.get(this.index + 1));
  104. }
  105.  
  106. public FormulaToken previous() {
  107. if (this.index < 1) return null;
  108. return (this.items.get(this.index - 1));
  109. }
  110.  
  111. }
  112.  
  113. public class TokenStack {
  114. public TokenStack() {
  115. this.items = new Stack<FormulaToken>();
  116. }
  117. public Stack<FormulaToken> items;
  118.  
  119.  
  120. public void push(FormulaToken token) {
  121. this.items.push(token);
  122. }
  123.  
  124. public FormulaToken pop() {
  125. FormulaToken token = this.items.pop();
  126. return (new FormulaToken("", token.type, TOK_SUBTYPE_STOP));
  127. }
  128.  
  129. public FormulaToken token() {
  130. return ((this.items.size() > 0) ? this.items.peek() : null);
  131. }
  132.  
  133. public String value() {
  134. return ((this.token() !=null ) ? this.token().value : "");
  135. }
  136.  
  137. public String type() {
  138. return ((this.token() !=null ) ? this.token().type : "");
  139. }
  140.  
  141. public String subtype() {
  142. return ((this.token() !=null ) ? this.token().subtype : "");
  143. }
  144.  
  145. }
  146.  
  147. private String currentChar() { return formula.substring(offset, offset+ 1); };
  148. private String doubleChar() { if(offset+1 == formula.length()) return currentChar(); return formula.substring(offset, offset+ 2); };
  149. private String nextChar() { return formula.substring(offset + 1, offset + 1); };
  150. private boolean EOF() { return (offset >= formula.length()); };
  151.  
  152.  
  153. public TokenList getTokens() {
  154.  
  155. TokenList tokens = new TokenList();
  156. TokenStack tokenStack = new TokenStack();
  157.  
  158.  
  159.  
  160.  
  161. String token = "";
  162.  
  163. boolean inString = false;
  164. boolean inPath = false;
  165. boolean inRange = false;
  166. boolean inError = false;
  167.  
  168. while (formula.length() > 0) {
  169. if (formula.startsWith(" "))
  170. formula = formula.substring(1);
  171. else {
  172. if (formula.startsWith("="))
  173. formula = formula.substring(1);
  174. break;
  175. }
  176. }
  177.  
  178. String regexSN = "^[1-9]{1}(\\.[0-9]+)?E{1}$";
  179.  
  180. while (!EOF()) {
  181.  
  182. // state-dependent character evaluation (order is important)
  183.  
  184. // double-quoted strings
  185. // embeds are doubled
  186. // end marks token
  187.  
  188. if (inString) {
  189. if (currentChar().equals("\"")) {
  190. if (nextChar().equals("\"")) {
  191. token += "\"";
  192. offset += 1;
  193. } else {
  194. inString = false;
  195. tokens.add(token, TOK_TYPE_OPERAND, TOK_SUBTYPE_TEXT);
  196. token = "";
  197. }
  198. } else {
  199. token += currentChar();
  200. }
  201. offset += 1;
  202. continue;
  203. }
  204.  
  205. // single-quoted strings (links)
  206. // embeds are double
  207. // end does not mark a token
  208.  
  209. if (inPath) {
  210. if (currentChar().equals("'")) {
  211. if (nextChar().equals("'")) {
  212. token += "'";
  213. offset += 1;
  214. } else {
  215. inPath = false;
  216. }
  217. } else {
  218. token += currentChar();
  219. }
  220. offset += 1;
  221. continue;
  222. }
  223.  
  224. // bracked strings (range offset or linked workbook name)
  225. // no embeds (changed to "()" by Excel)
  226. // end does not mark a token
  227.  
  228. if (inRange) {
  229. if (currentChar().equals("]")) {
  230. inRange = false;
  231. }
  232. token += currentChar();
  233. offset += 1;
  234. continue;
  235. }
  236.  
  237. // error values
  238. // end marks a token, determined from absolute list of values
  239.  
  240. if (inError) {
  241. token += currentChar();
  242. offset += 1;
  243. if ((",#NULL!,#DIV/0!,#VALUE!,#REF!,#NAME?,#NUM!,#N/A,").indexOf("," + token + ",") != -1) {
  244. inError = false;
  245. tokens.add(token, TOK_TYPE_OPERAND, TOK_SUBTYPE_ERROR);
  246. token = "";
  247. }
  248. continue;
  249. }
  250.  
  251. // scientific notation check
  252.  
  253. if (("+-").indexOf(currentChar()) != -1) {
  254. if (token.length() > 1) {
  255. if (token.matches(regexSN)) {
  256. token += currentChar();
  257. offset += 1;
  258. continue;
  259. }
  260. }
  261. }
  262.  
  263. // independent character evaulation (order not important)
  264.  
  265. // establish state-dependent character evaluations
  266.  
  267. if (currentChar().equals("\"")) {
  268. if (token.length() > 0) {
  269. // not expected
  270. tokens.add(token, TOK_TYPE_UNKNOWN);
  271. token = "";
  272. }
  273. inString = true;
  274. offset += 1;
  275. continue;
  276. }
  277.  
  278. if (currentChar().equals("'")) {
  279. if (token.length() > 0) {
  280. // not expected
  281. tokens.add(token, TOK_TYPE_UNKNOWN);
  282. token = "";
  283. }
  284. inPath = true;
  285. offset += 1;
  286. continue;
  287. }
  288.  
  289. if (currentChar().equals("[")) {
  290. inRange = true;
  291. token += currentChar();
  292. offset += 1;
  293. continue;
  294. }
  295.  
  296. if (currentChar().equals("#")) {
  297. if (token.length() > 0) {
  298. // not expected
  299. tokens.add(token, TOK_TYPE_UNKNOWN);
  300. token = "";
  301. }
  302. inError = true;
  303. token += currentChar();
  304. offset += 1;
  305. continue;
  306. }
  307.  
  308. // mark start and end of arrays and array rows
  309.  
  310. if (currentChar().equals("{")) {
  311. if (token.length() > 0) {
  312. // not expected
  313. tokens.add(token, TOK_TYPE_UNKNOWN);
  314. token = "";
  315. }
  316. tokenStack.push(tokens.add("ARRAY", TOK_TYPE_FUNCTION, TOK_SUBTYPE_START));
  317. tokenStack.push(tokens.add("ARRAYROW", TOK_TYPE_FUNCTION, TOK_SUBTYPE_START));
  318. offset += 1;
  319. continue;
  320. }
  321.  
  322. if (currentChar().equals(";")) {
  323. if (token.length() > 0) {
  324. tokens.add(token, TOK_TYPE_OPERAND);
  325. token = "";
  326. }
  327. tokens.addRef(tokenStack.pop());
  328. tokens.add(",", TOK_TYPE_ARGUMENT);
  329. tokenStack.push(tokens.add("ARRAYROW", TOK_TYPE_FUNCTION, TOK_SUBTYPE_START));
  330. offset += 1;
  331. continue;
  332. }
  333.  
  334. if (currentChar().equals("}")) {
  335. if (token.length() > 0) {
  336. tokens.add(token, TOK_TYPE_OPERAND);
  337. token = "";
  338. }
  339. tokens.addRef(tokenStack.pop());
  340. tokens.addRef(tokenStack.pop());
  341. offset += 1;
  342. continue;
  343. }
  344.  
  345. // trim white-space
  346.  
  347. if (currentChar().equals(" ")) {
  348. if (token.length() > 0) {
  349. tokens.add(token, TOK_TYPE_OPERAND);
  350. token = "";
  351. }
  352. tokens.add("", TOK_TYPE_WSPACE);
  353. offset += 1;
  354. while ((currentChar().equals(" ")) && (!EOF())) {
  355. offset += 1;
  356. }
  357. continue;
  358. }
  359.  
  360. // multi-character comparators
  361.  
  362. if ((",>=,<=,<>,").indexOf("," + doubleChar() + ",") != -1) {
  363. if (token.length() > 0) {
  364. tokens.add(token, TOK_TYPE_OPERAND);
  365. token = "";
  366. }
  367. tokens.add(doubleChar(), TOK_TYPE_OP_IN, TOK_SUBTYPE_LOGICAL);
  368. offset += 2;
  369. continue;
  370. }
  371.  
  372. // standard infix operators
  373.  
  374. if (("+-*/^&=><").indexOf(currentChar()) != -1) {
  375. if (token.length() > 0) {
  376. tokens.add(token, TOK_TYPE_OPERAND);
  377. token = "";
  378. }
  379. tokens.add(currentChar(), TOK_TYPE_OP_IN);
  380. offset += 1;
  381. continue;
  382. }
  383.  
  384. // standard postfix operators
  385.  
  386. if (("%").indexOf(currentChar()) != -1) {
  387. if (token.length() > 0) {
  388. tokens.add(token, TOK_TYPE_OPERAND);
  389. token = "";
  390. }
  391. tokens.add(currentChar(), TOK_TYPE_OP_POST);
  392. offset += 1;
  393. continue;
  394. }
  395.  
  396. // start subexpression or function
  397.  
  398. if (currentChar().equals("(")) {
  399. if (token.length() > 0) {
  400. tokenStack.push(tokens.add(token, TOK_TYPE_FUNCTION, TOK_SUBTYPE_START));
  401. token = "";
  402. } else {
  403. tokenStack.push(tokens.add("", TOK_TYPE_SUBEXPR, TOK_SUBTYPE_START));
  404. }
  405. offset += 1;
  406. continue;
  407. }
  408.  
  409. // function, subexpression, array parameters
  410.  
  411. if (currentChar().equals(",")) {
  412. if (token.length() > 0) {
  413. tokens.add(token, TOK_TYPE_OPERAND);
  414. token = "";
  415. }
  416. if (!(tokenStack.type() == TOK_TYPE_FUNCTION)) {
  417. tokens.add(currentChar(), TOK_TYPE_OP_IN, TOK_SUBTYPE_UNION);
  418. } else {
  419. tokens.add(currentChar(), TOK_TYPE_ARGUMENT);
  420. }
  421. offset += 1;
  422. continue;
  423. }
  424.  
  425. // stop subexpression
  426.  
  427. if (currentChar().equals(")")) {
  428. if (token.length() > 0) {
  429. tokens.add(token, TOK_TYPE_OPERAND);
  430. token = "";
  431. }
  432. tokens.addRef(tokenStack.pop());
  433. offset += 1;
  434. continue;
  435. }
  436.  
  437. // token accumulation
  438.  
  439. token += currentChar();
  440. offset += 1;
  441.  
  442. }
  443.  
  444. // dump remaining accumulation
  445.  
  446. if (token.length() > 0) tokens.add(token, TOK_TYPE_OPERAND);
  447.  
  448. // move all tokens to a new collection, excluding all unnecessary white-space tokens
  449.  
  450. TokenList tokens2 = new TokenList();
  451.  
  452. FormulaToken tokenObject;
  453.  
  454. while (tokens.moveNext()) {
  455.  
  456. tokenObject = tokens.current();
  457.  
  458. if (tokenObject.type == TOK_TYPE_WSPACE) {
  459. if ((tokens.BOF()) || (tokens.EOF())) {}
  460. else if (!(
  461. ((tokens.previous().type == TOK_TYPE_FUNCTION) && (tokens.previous().subtype == TOK_SUBTYPE_STOP)) ||
  462. ((tokens.previous().type == TOK_TYPE_SUBEXPR) && (tokens.previous().subtype == TOK_SUBTYPE_STOP)) ||
  463. (tokens.previous().type == TOK_TYPE_OPERAND)
  464. )
  465. ) {}
  466. else if (!(
  467. ((tokens.next().type == TOK_TYPE_FUNCTION) && (tokens.next().subtype == TOK_SUBTYPE_START)) ||
  468. ((tokens.next().type == TOK_TYPE_SUBEXPR) && (tokens.next().subtype == TOK_SUBTYPE_START)) ||
  469. (tokens.next().type == TOK_TYPE_OPERAND)
  470. )
  471. ) {}
  472. else
  473. tokens2.add(tokenObject.value, TOK_TYPE_OP_IN, TOK_SUBTYPE_INTERSECT);
  474. continue;
  475. }
  476.  
  477. tokens2.addRef(tokenObject);
  478.  
  479. }
  480.  
  481. // switch infix "-" operator to prefix when appropriate, switch infix "+" operator to noop when appropriate, identify operand
  482. // and infix-operator subtypes, pull "@" from in front of function names
  483.  
  484. while (tokens2.moveNext()) {
  485.  
  486. tokenObject = tokens2.current();
  487.  
  488. if ((tokenObject.type == TOK_TYPE_OP_IN) && tokenObject.value.equals("-")) {
  489. if (tokens2.BOF())
  490. tokenObject.type = TOK_TYPE_OP_PRE;
  491. else if (
  492. ((tokens2.previous().type == TOK_TYPE_FUNCTION) && (tokens2.previous().subtype == TOK_SUBTYPE_STOP)) ||
  493. ((tokens2.previous().type == TOK_TYPE_SUBEXPR) && (tokens2.previous().subtype == TOK_SUBTYPE_STOP)) ||
  494. (tokens2.previous().type == TOK_TYPE_OP_POST) ||
  495. (tokens2.previous().type == TOK_TYPE_OPERAND)
  496. )
  497. tokenObject.subtype = TOK_SUBTYPE_MATH;
  498. else
  499. tokenObject.type = TOK_TYPE_OP_PRE;
  500. continue;
  501. }
  502.  
  503. if ((tokenObject.type == TOK_TYPE_OP_IN) && (tokenObject.value.equals( "+"))) {
  504. if (tokens2.BOF())
  505. tokenObject.type = TOK_TYPE_NOOP;
  506. else if (
  507. ((tokens2.previous().type == TOK_TYPE_FUNCTION) && (tokens2.previous().subtype == TOK_SUBTYPE_STOP)) ||
  508. ((tokens2.previous().type == TOK_TYPE_SUBEXPR) && (tokens2.previous().subtype == TOK_SUBTYPE_STOP)) ||
  509. (tokens2.previous().type == TOK_TYPE_OP_POST) ||
  510. (tokens2.previous().type == TOK_TYPE_OPERAND)
  511. )
  512. tokenObject.subtype = TOK_SUBTYPE_MATH;
  513. else
  514. tokenObject.type = TOK_TYPE_NOOP;
  515. continue;
  516. }
  517.  
  518. if ((tokenObject.type == TOK_TYPE_OP_IN) && (tokenObject.subtype.length() == 0)) {
  519. if (("<>=").indexOf(tokenObject.value.substring(0, 1)) != -1)
  520. tokenObject.subtype = TOK_SUBTYPE_LOGICAL;
  521. else if (tokenObject.value.equals("&"))
  522. tokenObject.subtype = TOK_SUBTYPE_CONCAT;
  523. else
  524. tokenObject.subtype = TOK_SUBTYPE_MATH;
  525. continue;
  526. }
  527.  
  528. if ((tokenObject.type == TOK_TYPE_OPERAND) && (tokenObject.subtype.length() == 0)) {
  529. boolean isFloat = true;
  530. try {
  531. Float.parseFloat(tokenObject.value);
  532. } catch (NumberFormatException nfe) {
  533. isFloat = false;
  534. }
  535. if (isFloat == false)
  536. if ((tokenObject.value.equalsIgnoreCase("TRUE")) || (tokenObject.value.equalsIgnoreCase("FALSE")))
  537. tokenObject.subtype = TOK_SUBTYPE_LOGICAL;
  538. else
  539. tokenObject.subtype = TOK_SUBTYPE_RANGE;
  540. else
  541. tokenObject.subtype = TOK_SUBTYPE_NUMBER;
  542. continue;
  543. }
  544.  
  545. if (tokenObject.type == TOK_TYPE_FUNCTION) {
  546. if (tokenObject.value.startsWith("@"))
  547. tokenObject.value = tokenObject.value.substring(1);
  548. continue;
  549. }
  550.  
  551. }
  552.  
  553. tokens2.reset();
  554.  
  555. // move all tokens to a new collection, excluding all noops
  556.  
  557. tokens = new TokenList();
  558.  
  559. while (tokens2.moveNext()) {
  560. if (tokens2.current().type != TOK_TYPE_NOOP)
  561. tokens.addRef(tokens2.current());
  562. }
  563.  
  564. tokens.reset();
  565.  
  566. return tokens;
  567. }
  568.  
  569.  
  570.  
  571. }
  572.  
Compilation error #stdin compilation error #stdout 0s 0KB
stdin
Standard input is empty
compilation info
Main.java:7: error: class ParseFormula is public, should be declared in a file named ParseFormula.java
public class ParseFormula {
       ^
1 error
stdout
Standard output is empty