/** * * Copyright 2014 The Darks ORM Project (Liu lihua) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package darks.orm.core.data.tags.express; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; /** * �����沨��ʽ�㷨���б��ʽ���� * @author lihua.llh * */ public class RPNParser { static Map<String, Integer> operlevelMap = new HashMap<String, Integer>(); private static final char VAL_SEG = '\''; private static final char MIN_CHAR = 32; private static final String STR_SEG = " ()'|&!=<>+-*/%"; private static final String SPACE = " "; static { addOperLevel(1, "(", ")"); addOperLevel(2, "*", "/", "%"); addOperLevel(3, "+", "-"); addOperLevel(4, "<", "<=", ">", ">="); addOperLevel(5, "==", "!="); addOperLevel(6, "&", "&&"); addOperLevel(7, "|", "||"); } private int index; private LinkedList<String> operStack = new LinkedList<String>(); private LinkedList<String> valQueue = new LinkedList<String>(); private boolean valFlag = false; private static void addOperLevel(Integer level, String... opers) { for (String oper : opers) { operlevelMap.put(oper, level); } } public RPNParser() { reset(); } /** * ���ý����� */ public void reset() { operStack = new LinkedList<String>(); valQueue = new LinkedList<String>(); index = 0; } /** * �������ʽ * @param express ���ʽ * @return ���������� * @throws ExpressException */ public LinkedList<String> parse(String express) throws ExpressException { int len = express.length(); while (index < len) { char ch = express.charAt(index++); if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '\f') { continue; } switch (ch) { case '(': operStack.push(String.valueOf(ch)); break; case ')': parseToken(); break; case VAL_SEG: String val = parseValWithQuot(express).trim(); valQueue.offer(ExpressParam.STR_FLAG + val); validateFlag(true); break; default: processChars(parseChars(ch, express)); break; } } doFinal(); int size = valQueue.size(); if (1 < size && size < 3) { throw new ExpressException("Invalid express queue which loss operation." + valQueue); } return valQueue; } private void validateFlag(boolean actual) throws ExpressException { if (valFlag == actual) { throw new ExpressException("Invalid express.repeat value or operation"); } valFlag = actual; } private void doFinal() throws ExpressException { String op = null; while ((op = operStack.peek()) != null) { op = operStack.pop(); if ("(".equals(op)) { throw new ExpressException("Invalid RPN express. Loss match token ')'."); } valQueue.offer(op); } } private void processChars(String chars) throws ExpressException { Integer level = (Integer)operlevelMap.get(chars); if (level == null) { try { Integer.parseInt(chars); chars = ExpressParam.INT_FLAG + chars; } catch (Exception e) { try { Long.parseLong(chars); chars = ExpressParam.LONG_FLAG + chars; } catch (Exception ex) { } } valQueue.offer(chars); validateFlag(true); } else { processOper(chars, level); validateFlag(false); } } private void processOper(String oper, Integer level) throws ExpressException { String op = null; while ((op = operStack.peek()) != null) { if ("(".equals(op)) { break; } int oldLevel = (Integer)operlevelMap.get(op); if (level < oldLevel) { break; } operStack.pop(); valQueue.offer(op); } operStack.push(oper); } private String parseChars(char ch, String express) throws ExpressException { int len = express.length(); if (index < len) { char nextCh = express.charAt(index); String sch2 = String.valueOf(new char[]{ch, nextCh}); if (operlevelMap.containsKey(sch2)) { index++; return sch2; } } String sch = String.valueOf(ch); if (operlevelMap.containsKey(sch)) { return sch; } int start = index; StringBuilder buf = new StringBuilder(); buf.append(ch); while (index < len) { ch = express.charAt(index++); if (ch > MIN_CHAR && STR_SEG.indexOf(ch) < 0) { buf.append(ch); } else { index--; break; } } String op = buf.toString().trim(); if ("".equals(op)) { throw new ExpressException("Invalid RPN express near " + express.substring(Math.max(start - 3, 0), index) + ".Chars is empty."); } return op; } private void parseToken() throws ExpressException { boolean valid = false; String op = null; while ((op = operStack.peek()) != null) { op = operStack.pop(); if ("(".equals(op)) { valid = true; break; } valQueue.offer(op); } if (!valid) { throw new ExpressException("Invalid RPN express cause loss token '('"); } } private String parseValWithQuot(String express) throws ExpressException { int start = index; StringBuilder buf = new StringBuilder(); int len = express.length(); while (index < len) { char ch = express.charAt(index++); if (VAL_SEG == ch) { String val = buf.toString(); if ("".equals(val)) { throw new ExpressException("Invalid RPN express near " + express.substring(start, index) + ".Value is empty."); } return val; } else { buf.append(ch); } } throw new ExpressException("Invalid RPN express near " + express.substring(start, index) + ". Loss '\"' char."); } /** * ��ñ����ַ��� * @return �����ַ��� */ public String getCompile() { StringBuilder buf = new StringBuilder(); for (String val : valQueue) { val = val.replace(" ", SPACE); buf.append(val).append(' '); } buf.setLength(buf.length() - 1); return buf.toString(); } /** * ���������ַ��� * @param compile �����ַ��� * @return ������� */ public static LinkedList<String> parseCompile(String compile) { if (compile == null || "".equals(compile)) { return null; } LinkedList<String> linked = new LinkedList<String>(); String[] args = compile.split(" "); for (String arg : args) { arg = arg.replace(SPACE, " "); linked.offer(arg); } return linked; } }