/* * Copyright (c) 2010 SimpleServer authors (see CONTRIBUTORS) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package simpleserver.events; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import simpleserver.Player; import simpleserver.config.GiveAliasList.Item; import simpleserver.config.xml.Area; @SuppressWarnings("unused") public class PostfixEvaluator { private RunningEvent e; private ArrayList<String> expstack; private static HashMap<String, String> ops; PostfixEvaluator(RunningEvent context) { e = context; expstack = new ArrayList<String>(); if (ops == null) { initOps(); } } private void initOps() { ops = new HashMap<String, String>(); // maps operator token -> method name ops.put("not", "not"); ops.put("and", "and"); ops.put("or", "or"); ops.put("+", "add"); ops.put("-", "sub"); ops.put("*", "mul"); ops.put("/", "div"); ops.put("%", "mod"); ops.put("gt", "gt"); ops.put("lt", "lt"); ops.put("eq", "eq"); ops.put(".", "strcat"); ops.put("..", "strcatspace"); ops.put("isempty", "isempty"); ops.put("length", "length"); ops.put("int", "str2num"); ops.put("bool", "str2bool"); ops.put("rand", "rand"); ops.put("currtime", "currtime"); ops.put("totime", "int2timestr"); ops.put("evalvar", "evalvar"); ops.put("getitemalias", "id2alias"); ops.put("getitemid", "alias2id"); ops.put("dup", "dup"); ops.put("drop", "drop"); ops.put("flip", "flip"); ops.put("getgroup", "getgroup"); ops.put("getplayers", "getplayers"); ops.put("isinarea", "isinarea"); ops.put("getcoord", "getcoord"); /* array ops */ ops.put("Anew", "arraynew"); ops.put("Acreate", "arraycreate"); ops.put("Apush", "arraypush"); ops.put("Apop", "arraypop"); ops.put("Ainsert", "arrayinsert"); ops.put("Aremove", "arrayremove"); ops.put("Asize", "arraysize"); ops.put("Aisempty", "arrayisempty"); ops.put("Acontains", "arraycontains"); ops.put("Aget", "arrayget"); ops.put("Agetlast", "arraygetlast"); ops.put("Ajoin", "arrayjoin"); ops.put("Aexplode", "arrayexplode"); /* hash ops */ ops.put("Hnew", "hashnew"); ops.put("Hcreate", "hashcreate"); ops.put("Hput", "hashput"); ops.put("Hget", "hashget"); ops.put("Hremove", "hashremove"); ops.put("Hcontainskey", "hashcontainskey"); ops.put("Hcontainsvalue", "hashcontainsval"); ops.put("Hisempty", "hashisempty"); ops.put("Hsize", "hashsize"); ops.put("Hkeys", "hashkeys"); ops.put("Hvalues", "hashvalues"); /* check that all methods really exist... */ try { for (String name : ops.values()) { java.lang.reflect.Method m = this.getClass().getDeclaredMethod(name, new Class[] {}); } } catch (NoSuchMethodException e) { System.out.println("Error in event interpreter - stack operator method mappings faulty!"); System.out.println("Please file a bug report!"); } } /*---- push + pop helpers ----*/ private String pop() throws IndexOutOfBoundsException { return expstack.remove(0); } private Long popNum() throws IndexOutOfBoundsException { return toNum(expstack.remove(0)); } private Boolean popBool() throws IndexOutOfBoundsException { return toBool(expstack.remove(0)); } private ArrayList<String> popArray() throws IndexOutOfBoundsException { return toArray(expstack.remove(0)); } private HashMap<String, String> popHash() throws IndexOutOfBoundsException { return toHash(expstack.remove(0)); } private void push(String val) { expstack.add(0, String.valueOf(val)); } private void push(long val) { expstack.add(0, String.valueOf(val)); } private void push(boolean val) { expstack.add(0, String.valueOf(val)); } private void push(ArrayList<String> val) { expstack.add(0, fromArray(val)); } private void push(HashMap<String, String> val) { expstack.add(0, fromHash(val)); } /*---- interface ----*/ /* evaluate, return all elements left at the end */ public ArrayList<String> evaluate(ArrayList<String> tokens) { try { while (tokens.size() > 0) { String elem = tokens.remove(0); if (elem.charAt(0) == RunningEvent.REFERENCEOP) { // escape->as string elem = elem.substring(1); push(elem); continue; } if (ops.containsKey(elem)) { // look through methods this.getClass().getDeclaredMethod(ops.get(elem), new Class[] {}).invoke(this); } else { // no method in hash -> regular value push(elem); } } } catch (Exception ex) { e.notifyError("Invalid expression!"); return null; } ArrayList<String> ret = new ArrayList<String>(); for (String s : expstack) { ret.add(0, s); } return ret; } /* evaluate, return top element */ public String evaluateSingle(ArrayList<String> tokens) { tokens = evaluate(tokens); if (expstack.size() == 0) { e.notifyError("Expression returns no value! Returning null."); return null; } else if (expstack.size() > 1) { e.notifyError("Expression returns multiple values! Returning last."); } return pop(); // one element left -> correct evaluated result } /* evaluate stack, use as boolean expression */ public boolean evaluateCondition(ArrayList<String> tokens) { return toBool(evaluateSingle(tokens)); } /*---- Common data type methods ----*/ public static boolean toBool(String bool) { if (bool.equals("false") || bool.equals("null") || bool.equals("") || bool.equals("0")) { return false; } return true; } public static long toNum(String num) { long ret = 0; try { ret = Long.valueOf(num); } catch (Exception e) { if (num.equals("true")) { return 1; } else { return 0; } } return ret; } /* format time string from milliseconds (max. unit=days) */ public static String fmtTime(long ms) { long millis = ms % 1000; ms -= millis; ms /= 1000; long secs = ms % 60; ms -= secs; ms /= 60; long mins = ms % 60; ms -= mins; ms /= 60; long hrs = ms % 24; ms -= hrs; ms /= 24; long days = ms; String time = ""; if (days != 0) { time += String.valueOf(days) + "d "; } if (hrs != 0) { time += String.valueOf(hrs) + "h "; } if (mins != 0) { time += String.valueOf(mins) + "m "; } if (secs != 0) { time += String.valueOf(secs) + "s "; } if (millis != 0) { time += String.valueOf(millis) + "ms "; } return time; } public static ArrayList<String> toArray(String val) { if (val == null || val.length() < 2 || val.charAt(0) != '[' || val.charAt(val.length() - 1) != ']') { return new ArrayList<String>(); // not valid array -> return empty one } ArrayList<String> arr = new ArrayList<String>(); val = val.substring(1, val.length() - 1); for (String s : val.split("(?<!\\\\),")) { s = unescape(s, ","); if (!s.equals("")) { arr.add(s); } } return arr; } public static String fromArray(ArrayList<String> val) { String s = "["; while (val.size() != 0) { String t = val.remove(0); t = escape(t, ","); s += t; if (val.size() != 0) { s += ","; } } s += "]"; return s; } public static String fromHash(HashMap<String, String> val) { String s = "{"; if (val.size() > 0) { for (String key : val.keySet()) { String t = val.get(key); key = escape(key, ",:"); t = escape(t, ",:"); s += key + ":" + t + ","; } s = s.substring(0, s.length() - 1); } s += "}"; return s; } public static HashMap<String, String> toHash(String val) { if (val == null || val.length() < 2 || val.charAt(0) != '{' || val.charAt(val.length() - 1) != '}') { return new HashMap<String, String>(); // not valid hash -> return empty // one } HashMap<String, String> ret = new HashMap<String, String>(); val = val.substring(1, val.length() - 1); for (String s : val.split("(?<!\\\\),")) { String[] toks = s.split("(?<!\\\\):"); if (toks.length != 2) { return new HashMap<String, String>(); } ret.put(unescape(toks[0], ",:"), unescape(toks[1], ",:")); } return ret; } /*---- generic (un)escape for array/hash/etc ----*/ public static String escape(String str, String chars) { String ret = str; for (int i = 0; i < chars.length(); i++) { String c = chars.substring(i, i + 1); ret = ret.replaceAll(c, "\\\\" + c); } return ret; } public static String unescape(String str, String chars) { String ret = str; for (int i = 0; i < chars.length(); i++) { String c = chars.substring(i, i + 1); ret = ret.replaceAll("\\\\" + c, c); } return ret; } /*---- Operators ----*/ /* boolean ops */ private void not() { push(!popBool()); } private void and() { boolean b2 = popBool(); boolean b1 = popBool(); push(b1 && b2); } private void or() { boolean b2 = popBool(); boolean b1 = popBool(); push(b1 || b2); } /* number ops */ private void add() { long d2 = popNum(); long d1 = popNum(); push(d1 + d2); } private void sub() { long d2 = popNum(); long d1 = popNum(); push(d1 - d2); } private void mul() { long d2 = popNum(); long d1 = popNum(); push(d1 * d2); } private void div() { long d2 = popNum(); long d1 = popNum(); push(d1 / d2); } private void mod() { long d2 = popNum(); long d1 = popNum(); push(d1 % d2); } private void rand() { long d = popNum(); push(e.eventHost.rng.nextInt((int) d)); } /* comparison ops */ private void gt() { long d2 = popNum(); long d1 = popNum(); push(d1 > d2); } private void lt() { long d2 = popNum(); long d1 = popNum(); push(d1 < d2); } private void eq() { String v2 = pop(); String v1 = pop(); push(v1.equals(v2)); } /* string ops */ private void strcat() { String s2 = pop(); String s1 = pop(); push(s1 + s2); } private void strcatspace() { String s2 = pop(); String s1 = pop(); push(s1 + " " + s2); } private void isempty() { push(pop().equals("")); } private void length() { push(pop().length()); } /* other ops */ private void str2num() { push(popNum()); } private void str2bool() { push(popBool()); } private void currtime() { push(String.valueOf(System.currentTimeMillis())); } private void int2timestr() { push(fmtTime(popNum())); } private void evalvar() { String s = pop(); String v = e.evaluateVar(s); if (v != null) { push(v); } else { e.notifyError("Warning: Invalid variable: " + s + "! Returning null"); push("null"); } } private void id2alias() { String[] id = pop().split(":"); String alias = "null"; if (id.length == 1) { alias = e.server.giveAliasList.getAlias(Integer.valueOf(id[0]), 0); } else { alias = e.server.giveAliasList.getAlias(Integer.valueOf(id[0]), Integer.valueOf(id[1])); } push(alias); } private void alias2id() { String alias = pop(); Item i = e.server.giveAliasList.getItemId(alias); if (i != null) { String id = String.valueOf(i.id); if (i.damage != 0) { id += ":" + String.valueOf(i.damage); } push(id); } else { push("null"); } } private void dup() { String s = pop(); push(s); push(s); } private void drop() { pop(); } private void flip() { if (expstack.size() < 2) { return; } String b = pop(); String a = pop(); push(b); push(a); } private void getgroup() { String s = pop(); Player tmp = e.server.findPlayer(s); if (tmp == null) { e.notifyError("getgroup: Player not found! Returning group -1"); push(-1); } else { push(tmp.getGroupId()); } } private void getplayers() { ArrayList<String> players = new ArrayList<String>(); for (Player p : e.server.playerList.getArray()) { players.add(p.getName()); } push(players); } private void isinarea() { String area = pop(); String player = pop(); Player p = e.server.findPlayer(player); if (p == null) { e.notifyError("isarea: Player not found!"); push(false); } HashSet<Area> areas = new HashSet<Area>(e.server.config.dimensions.areas(p.position())); for (Area a : areas) { if (a.name.equals(area)) { push(true); return; } } push(false); return; } private void getcoord() { String player = pop(); Player p = e.server.findPlayer(player); if (p == null) { e.notifyError("isarea: Player not found!"); push(false); } ArrayList<String> c = new ArrayList<String>(); c.add(String.valueOf(p.position().x())); c.add(String.valueOf(p.position().y())); c.add(String.valueOf(p.position().z())); push(c); } /* array ops */ private void arraynew() { push(new ArrayList<String>()); } private void arraycreate() { long num = popNum(); ArrayList<String> arr = new ArrayList<String>(); for (int i = 0; i < num; i++) { arr.add(0, pop()); } push(arr); } private void arraypush() { String val = pop(); ArrayList<String> arr = popArray(); arr.add(val); push(arr); } private void arraypop() { ArrayList<String> arr = popArray(); if (arr.size() != 0) { arr.remove(arr.size() - 1); push(arr); } else { e.notifyError("Popping empty array!"); } } private void arrayinsert() { String val = pop(); int index = popNum().intValue(); ArrayList<String> arr = popArray(); if (index >= 0 && arr.size() >= index) { arr.add(index, val); } else { e.notifyError("Index out of range!"); } push(arr); } private void arrayremove() { int index = popNum().intValue(); ArrayList<String> arr = popArray(); if (index >= 0 && arr.size() > index) { arr.remove(index); } else { e.notifyError("Index out of range!"); } push(arr); } private void arrayget() { int index = popNum().intValue(); ArrayList<String> arr = popArray(); if (index >= 0 && arr.size() > index) { push(unescape(arr.get(index), ",")); } else { e.notifyError("Index out of range!"); push("null"); } } private void arraycontains() { String val = pop(); ArrayList<String> arr = popArray(); push(arr.contains(val)); } private void arraygetlast() { ArrayList<String> arr = popArray(); if (arr.size() != 0) { push(arr.remove(arr.size() - 1)); } else { e.notifyError("Popping empty array!"); push("null"); } } private void arraysize() { ArrayList<String> arr = popArray(); push(arr.size()); } private void arrayisempty() { ArrayList<String> arr = popArray(); push(arr.size() == 0); } private void arrayjoin() { String delimeter = pop(); ArrayList<String> arr = popArray(); String s = ""; while (arr.size() != 0) { s += unescape(arr.remove(0), ","); if (arr.size() != 0) { s += delimeter; } } push(s); } private void arrayexplode() { ArrayList<String> arr = popArray(); if (arr.size() > 0) { for (String e : arr) { push(e); } } else { push("null"); } } /* hash ops */ private void hashnew() { push(new HashMap<String, String>()); } private void hashcreate() { long num = popNum(); HashMap<String, String> h = new HashMap<String, String>(); for (int i = 0; i < num; i++) { String val = pop(); String key = pop(); if (!key.equals("") && !key.equals("null")) { h.put(key, val); } else { e.notifyError("Invalid hash key! Ignoring hash pair..."); } } push(h); } private void hashput() { String val = pop(); String key = pop(); HashMap<String, String> h = popHash(); if (!key.equals("") && !key.equals("null")) { h.put(key, val); } else { e.notifyError("Invalid hash key!"); } push(h); } private void hashget() { String key = pop(); HashMap<String, String> h = popHash(); if (!key.equals("") && !key.equals("null")) { String val = h.get(key); if (val == null) { val = "null"; } push(val); } else { e.notifyError("Invalid hash key!"); push("null"); } } private void hashremove() { String key = pop(); HashMap<String, String> h = popHash(); if (!key.equals("") && !key.equals("null")) { h.remove(key); } else { e.notifyError("Invalid hash key!"); } push(h); } private void hashcontainskey() { String key = pop(); HashMap<String, String> h = popHash(); if (!key.equals("") && !key.equals("null")) { push(h.containsKey(key)); } else { e.notifyError("Invalid hash key!"); push(false); } } private void hashcontainsval() { String val = pop(); HashMap<String, String> h = popHash(); push(h.containsValue(val)); } private void hashisempty() { HashMap<String, String> h = popHash(); push(h.size() == 0); } private void hashsize() { HashMap<String, String> h = popHash(); push(h.size()); } private void hashkeys() { HashMap<String, String> h = popHash(); push(new ArrayList<String>(h.keySet())); } private void hashvalues() { HashMap<String, String> h = popHash(); push(new ArrayList<String>(h.values())); } }