/** * Title: AGG<p> * Description: <p> * Copyright: Copyright (c) Michael Matz<p> * Company: TU Berlin<p> * @author Michael Matz * @version 1.0 */ package agg.cons; import java.text.StringCharacterIterator; import java.text.CharacterIterator; import java.util.HashMap; import java.util.List; import java.util.Vector; import java.io.Serializable; import agg.util.XMLHelper; import agg.util.XMLObject; import agg.xt_basis.NestedApplCond; @SuppressWarnings("serial") public class Formula implements Evaluable, XMLObject, Serializable { public static final int NOP = 0; public static final int TRUE = 1; public static final int FALSE = 2; public static final int NOT = 3; public static final int AND = 4; public static final int OR = 5; public static final int EXISTS = 6; public static final int FORALL = 7; public static final int UNDEF = -1; private Evaluable sub1, sub2; private int op; public String str = ""; private int running_tick; private String name = ""; private boolean evaluable = true; private boolean enabled = true; private String comment = ""; private Vector<Integer> layer = new Vector<Integer>(5); private Vector<Integer> priority = new Vector<Integer>(5); private void init(int _op, Evaluable e1, Evaluable e2) { this.op = _op; this.sub1 = e1; this.sub2 = e2; } public Formula(boolean v) { init(v ? TRUE : FALSE, null, null); /* * _Don't_ initialize running_tick in init(). It does not belong to the * Formula itself, but rather to it's sub parts. Resetting it after the * constructor might make subparts return a cached result, although * called with a different object. */ this.running_tick = 0; this.name = "Formula"; this.evaluable = true; this.enabled = true; this.str = String.valueOf(v); } public Formula(List<Evaluable> vars, String s) { setFormula(vars, s); this.running_tick = 0; this.name = "Formula"; this.evaluable = true; this.enabled = true; this.str = s; } /* Creates a formula connecting all vars with operator op. */ public Formula(List<Evaluable> vars, int op) { this.running_tick = 0; if (op != AND && op != OR) { init(UNDEF, null, null); return; } if (vars.isEmpty()) { init(TRUE, null, null); this.str = String.valueOf(true); } else { String s = "1"; for (int i = 1; i < vars.size(); i++) { s += (op == AND) ? " && " : " || "; s += Integer.toString(i + 1); } setFormula(vars, s); this.str = s; } this.name = "Formula"; this.evaluable = true; this.enabled = true; } public void setName(String str) { this.name = str; } public String getName() { return this.name; } /** Set textual comments for this formula. */ public void setTextualComment(String text) { this.comment = text; } /** Return textual comments of this formula. */ public String getTextualComment() { return this.comment; } public void setEnabled(boolean b) { this.enabled = b; } public boolean isEnabled() { return this.enabled; } public boolean setFormula(List<Evaluable> vars, String s) { // System.out.println(s); Formula that = get_multiple(vars, new StringCharacterIterator(s)); if (that != null) { init(that.op, that.sub1, that.sub2); return true; } init(UNDEF, null, null); return false; } public String getAsString(List<Evaluable> v) { HashMap<Evaluable, String> m = new HashMap<Evaluable, String>(); for (int i = 0; i < v.size(); i++) { m.put(v.get(i), Integer.toString(i + 1, 10)); } return getAsString(v, m); } public String getAsString(List<Evaluable> v, List<String> names) { if (names.size() != v.size()) { return getAsString(v); } final HashMap<Evaluable, String> map = new HashMap<Evaluable, String>(); for (int i = 0; i < v.size(); i++) { map.put(v.get(i), names.get(i)); } return getAsReadableString(v, map); } public String getAsReadableString(List<Evaluable> v) { String s1 = ""; String s2 = ""; switch (this.op) { case UNDEF: return ""; case NOT: // return "(" + "!" + ((Formula) this.sub1).getAsReadableString(v, map) + ")"; case FORALL: // s1 = ((Formula) this.sub1).getAsReadableString(v, map); return "FORALL" + "( " + s1 + " )"; case EXISTS: // s1 = ((Formula) this.sub1).getAsReadableString(v, map); return "EXISTS" + "( " + s1 + " )"; case FALSE: return "false"; case TRUE: return "true"; case AND: case OR: // s1 = ((Formula) this.sub1).getAsReadableString(v, map); // s2 = ((Formula) this.sub2).getAsReadableString(v, map); return "(" + s1 + ((this.op == AND) ? " & " : " | ") + s2 + ")"; case NOP: // if (this.sub1 instanceof Formula) // return "(" + ((Formula) this.sub1).getAsString(v, map) + ")"; // String n; // if (!map.containsKey(this.sub1)) { // int i = v.size(); // v.add(this.sub1); // n = Integer.toString(i + 1, 10); // map.put(this.sub1, n); // } else // n = map.get(this.sub1); // return n; } return "true"; } private String getAsReadableString(List<Evaluable> v, HashMap<Evaluable, String> map) { String s1 = ""; String s2 = ""; switch (this.op) { case UNDEF: return ""; case NOT: return "(" + "!" + ((Formula) this.sub1).getAsReadableString(v, map) + ")"; case FORALL: s1 = ((Formula) this.sub1).getAsReadableString(v, map); return "FORALL" + "( " + s1 + " )"; case EXISTS: s1 = ((Formula) this.sub1).getAsReadableString(v, map); return "EXISTS" + "( " + s1 + " )"; case FALSE: return "false"; case TRUE: return "true"; case AND: case OR: s1 = ((Formula) this.sub1).getAsReadableString(v, map); s2 = ((Formula) this.sub2).getAsReadableString(v, map); return "(" + s1 + ((this.op == AND) ? " & " : " | ") + s2 + ")"; case NOP: if (this.sub1 instanceof Formula) return "(" + ((Formula) this.sub1).getAsString(v, map) + ")"; String n; if (!map.containsKey(this.sub1)) { int i = v.size(); v.add(this.sub1); n = Integer.toString(i + 1, 10); map.put(this.sub1, n); } else n = map.get(this.sub1); return n; } return "true"; } public boolean compareTo(Formula f) { if (!getName().equals(f.getName())) return false; Vector<Evaluable> vec = new Vector<Evaluable>(); String form = getAsString(vec); String form1 = f.getAsString(vec); if (!form.equals(form1)) return false; return true; } private boolean isValid(Evaluable e) { if (e == null) return false; if (!(e instanceof Formula)) return true; return ((Formula) e).isValid(); } public boolean isValid() { this.evaluable = true; switch (this.op) { case UNDEF: return false; case TRUE: case FALSE: return true; case NOP: case NOT: case FORALL: case EXISTS: return isValid(this.sub1); case AND: case OR: return (isValid(this.sub1) && isValid(this.sub2)); } return false; } private void setEvaluable(Evaluable e) { if (e instanceof AtomConstraint) { this.evaluable = this.evaluable && ((AtomConstraint) e).isEvaluable(); } else if (e instanceof Formula) { this.evaluable = this.evaluable && ((Formula) e).isEvaluable(); } else if (e instanceof NestedApplCond) { this.evaluable = this.evaluable && ((NestedApplCond) e).isEvaluable(); } } public boolean isEvaluable() { return this.evaluable; } public boolean eval(java.lang.Object o) { return eval(o, -1); } public boolean eval(java.lang.Object o, int t) { int tick = t; if (tick == -1) { if (this.running_tick < 0) this.running_tick = 0; tick = this.running_tick++; } switch (this.op) { case NOP: if (this.sub1 != null) { boolean result = this.sub1.eval(o, tick); setEvaluable(this.sub1); return result; } return false; case NOT: if (this.sub1 != null) { boolean result = !this.sub1.eval(o, tick, true); setEvaluable(this.sub1); return result; } return false; case AND: if ((this.sub1 != null) && (this.sub2 != null)) { boolean result = this.sub1.eval(o, tick) && this.sub2.eval(o, tick); setEvaluable(this.sub1); setEvaluable(this.sub2); return result; } return false; case OR: if (this.sub1 != null) { boolean result = this.sub1.eval(o, tick); setEvaluable(this.sub1); if (result) return result; } if (this.sub2 != null) { boolean result = this.sub2.eval(o, tick); setEvaluable(this.sub2); if (result) return result; } return false; case FORALL: if (this.sub1 != null) { boolean result = this.sub1.evalForall(o, tick); setEvaluable(this.sub1); return result; } return false; case EXISTS: if (this.sub1 != null) { boolean result = this.sub1.eval(o, tick); setEvaluable(this.sub1); return result; } return false; case TRUE: return true; case FALSE: return false; } return false; } public boolean eval(java.lang.Object o, boolean negation) { return eval(o, -1, negation); } public boolean eval(java.lang.Object o, int t, boolean negation) { int tick = t; if (tick == -1) { if (this.running_tick < 0) this.running_tick = 0; tick = this.running_tick++; } switch (this.op) { case NOP: if (this.sub1 != null) { boolean result = this.sub1.eval(o, tick, negation); setEvaluable(this.sub1); return result; } return false; case NOT: if (this.sub1 != null) { boolean result = !this.sub1.eval(o, tick, true); setEvaluable(this.sub1); return result; } return false; case AND: if ((this.sub1 != null) && (this.sub2 != null)) { boolean result = this.sub1.eval(o, tick, false) && this.sub2.eval(o, tick, false); setEvaluable(this.sub1); setEvaluable(this.sub2); return result; } return false; case OR: if (this.sub1 != null) { boolean result = this.sub1.eval(o, tick, false); setEvaluable(this.sub1); if (result) return result; } if (this.sub2 != null) { boolean result = this.sub2.eval(o, tick, false); setEvaluable(this.sub2); if (result) return result; } return false; case FORALL: if (this.sub1 != null) { boolean result = this.sub1.evalForall(o, tick); setEvaluable(this.sub1); return result; } return false; case EXISTS: if (this.sub1 != null) { boolean result = this.sub1.eval(o, tick); setEvaluable(this.sub1); return result; } return false; case TRUE: return true; case FALSE: return false; } return false; } /* * This is used, when an Atomic is deleted, to make the formulas valid * again. All occurrences of Evaluable e in the formula are patched out by a constant * formula, which according to the second argument is either true or false. */ public void patchOutEvaluable(Evaluable e, boolean subst) { switch (this.op) { case AND: case OR: if (this.sub1 == e) this.sub1 = new Formula(subst); else if (this.sub1 != null && this.sub1 instanceof Formula) ((Formula) this.sub1).patchOutEvaluable(e, subst); if (this.sub2 == e) this.sub2 = new Formula(subst); else if (this.sub2 != null && this.sub2 instanceof Formula) ((Formula) this.sub2).patchOutEvaluable(e, subst); /* Fallthrough */ case NOT: if (this.sub1 == e) this.sub1 = new Formula(!subst); else if (this.sub1 != null && this.sub1 instanceof Formula) ((Formula) this.sub1).patchOutEvaluable(e, !subst); case NOP: case FORALL: case EXISTS: if (this.sub1 == e) this.sub1 = new Formula(subst); else if (this.sub1 != null && this.sub1 instanceof Formula) ((Formula) this.sub1).patchOutEvaluable(e, subst); } } public void patchInEvaluableAsDisjunction(Evaluable e, List<Evaluable> subst) { String s = ""; if (subst.size() > 0) s = "1"; for (int i=1; i<subst.size(); i++) { s = s + "|" + String.valueOf(i+1); } // System.out.println("Formula: "+s); Formula f = new Formula(subst, s); switch (this.op) { case AND: case OR: if (this.sub1 == e) this.sub1 = f; else if (this.sub1 != null && this.sub1 instanceof Formula) ((Formula) this.sub1).patchInEvaluableAsDisjunction(e, subst); if (this.sub2 == e) this.sub2 = f; else if (this.sub2 != null && this.sub2 instanceof Formula) ((Formula) this.sub2).patchInEvaluableAsDisjunction(e, subst); case NOT: if (this.sub1 == e) this.sub1 = f; else if (this.sub1 != null && this.sub1 instanceof Formula) ((Formula) this.sub1).patchInEvaluableAsDisjunction(e, subst); case NOP: case FORALL: case EXISTS: if (this.sub1 == e) this.sub1 = f; else if (this.sub1 != null && this.sub1 instanceof Formula) ((Formula) this.sub1).patchInEvaluableAsDisjunction(e, subst); } } public Formula(int _op, Evaluable e1, Evaluable e2) { init(_op, e1, e2); } private String getAsString(List<Evaluable> v, HashMap<Evaluable, String> map) { String s1 = ""; String s2 = ""; switch (this.op) { case UNDEF: return ""; case NOT: return "(" + "!" + ((Formula) this.sub1).getAsString(v, map) + ")"; case FORALL: s1 = ((Formula) this.sub1).getAsString(v, map); return "A" + "( " + s1 + " )"; case EXISTS: s1 = ((Formula) this.sub1).getAsString(v, map); return "E" + "( " + s1 + " )"; case FALSE: return "F"; //"false"; case TRUE: return "T"; //"true"; case AND: s1 = ((Formula) this.sub1).getAsString(v, map); s2 = ((Formula) this.sub2).getAsString(v, map); return "( " + s1 + " & " + s2 + " )"; case OR: s1 = ((Formula) this.sub1).getAsString(v, map); s2 = ((Formula) this.sub2).getAsString(v, map); return "( " + s1 + " | " + s2 + " )"; case NOP: if (this.sub1 instanceof Formula) return "(" + ((Formula) this.sub1).getAsString(v, map) + ")"; String n; if (!map.containsKey(this.sub1)) { int i = v.size(); v.add(this.sub1); n = Integer.toString(i + 1, 10); map.put(this.sub1, n); } else n = map.get(this.sub1); return n; } return "true"; } private void skipWS(StringCharacterIterator i) { char c = i.current(); while (c == ' ' || c == '\t' || c == '\n' || c == '\r') c = i.next(); } private Formula get_multiple(List<Evaluable> vars, StringCharacterIterator i) { Formula ret = get_one(vars, i); while (ret != null) { char c = i.current(); if (c == CharacterIterator.DONE) { break; } else if (c == '&' || c == '|') { while (i.current() == c) i.next(); Formula f2 = get_one(vars, i); if (f2 == null) ret = null; else ret = new Formula(c == '&' ? AND : OR, ret, f2); } else if (c == 'A') { i.next(); // "(" // i.next(); // "$" // i.next(); // "," c = i.next(); Formula f = get_one(vars, i); if (f == null) return null; return new Formula(FORALL, f, null); } else if (c == 'E') { i.next(); // "(" // i.next(); // "$" // i.next(); // "," c = i.next(); Formula f = get_one(vars, i); if (f == null) return null; return new Formula(EXISTS, f, null); } else if (c == 'T') { return get_one(vars, i); } else if (c == 'F') { return get_one(vars, i); } else if (c == ')') { c = i.next(); skipWS(i); break; } else { skipWS(i); } } return ret; } private Formula get_one(List<Evaluable> vars, StringCharacterIterator i) { skipWS(i); char c = i.current(); if (c == '(') { c = i.next(); if (c == CharacterIterator.DONE) return null; Formula f = get_multiple(vars, i); if (i.current() == ')') c = i.next(); return f; } else if (c == '!') { c = i.next(); Formula f = get_one(vars, i); if (f == null) return null; return new Formula(NOT, f, null); } else if (c == 'A') { i.next(); // "(" // skipWS(i); // i.next(); // "$" // skipWS(i); // i.next(); // "," // skipWS(i); c = i.next(); Formula f1 = get_one(vars, i); if (f1 == null) return null; return new Formula(FORALL, f1, null); } else if (c == 'E') { i.next(); // "(" // skipWS(i); // i.next(); // "$" // skipWS(i); // i.next(); // "," // skipWS(i); c = i.next(); Formula f1 = get_one(vars, i); if (f1 == null) return null; return new Formula(EXISTS, f1, null); } else if (c == 'T') { c = i.next(); return new Formula(true); } else if (c == 'F') { c = i.next(); return new Formula(false); } else if (c >= '0' && c <= '9') { int v = 0; while (c >= '0' && c <= '9') { v = v * 10 + (c - '0'); c = i.next(); } skipWS(i); v--; if (v < 0 || v >= vars.size()) return null; return new Formula(NOP, vars.get(v), null); } else if (c == 'f' || c == 't') { while (i.current() >= 'a' && i.current() <= 'z') i.next(); skipWS(i); return new Formula(c == 't'); } else return null; } public static List<Integer> getFromStringAboveList(String s, final List<String> list) { final List<Integer> result = new Vector<Integer>(2); final List<String> edit = new Vector<String>(5,2); StringCharacterIterator i = new StringCharacterIterator(s); char c = i.current(); while (c != CharacterIterator.DONE) { if (c == '&' || c == '|' || c == '!' || c == '$' || c == 'A' || c == 'E' || c == ' ' || c == ',' || c == '(' || c == ')') { edit.add(String.valueOf(c)); i.next(); } else if (c >= '0' && c <= '9') { String cs = ""; int v = 0; while (c >= '0' && c <= '9') { cs = cs.concat(String.valueOf(c)); v = v * 10 + (c - '0'); c = i.next(); } v--; if (v < 0 || v >= list.size()) return result; edit.add(String.valueOf(cs)); } else if (c == 'f' || c == 't') { String cs = ""; while (i.current() >= 'a' && i.current() <= 'z') { cs = cs.concat(String.valueOf(c)); i.next(); } edit.add(String.valueOf(cs)); } c = i.current(); } for (int j=0; j<edit.size(); j++) { try { result.add(Integer.valueOf(edit.get(j))); } catch (Exception ex) {} } return result; } public int getOperation() { return this.op; } public Evaluable getFirst() { return this.sub1; } public Evaluable getSecond() { return this.sub2; } public boolean isNOT(Evaluable var, Vector<Evaluable> vars) { boolean b = false; switch (this.op) { case UNDEF: return false; case FALSE: return false; case TRUE: return false; case NOT: case FORALL: case EXISTS: b = ((Formula) this.sub1).isNOT(var, vars); return b; case AND: case OR: b = ((Formula) this.sub1).isNOT(var, vars); if (!b) b = ((Formula) this.sub2).isNOT(var, vars); return b; case NOP: if (this.sub1 instanceof Formula) b = ((Formula) this.sub1).isNOT(var, vars); else if (vars.contains(this.sub1) && this.sub1 == var) { b = true; } return b; } return b; } /** * Returns my layer. The layer is used by layered grammar. */ public Vector<Integer> getLayer() { return this.layer; } public String getLayerAsString() { String str = ""; for (int k = 0; k < this.layer.size(); k++) { int l = this.layer.get(k).intValue(); str = str + String.valueOf(l); if (k < this.layer.size() - 1) str = str + ","; } return str; } /** * Add the specified layer l to its layer container. */ public void addLayer(int l) { boolean added = false; for (int i = 0; i < this.layer.size(); i++) { if (l <= this.layer.get(i).intValue()) { this.layer.add(i, new Integer(l)); added = true; break; } } if (!added) this.layer.add(new Integer(l)); } /** * Set the specified Vector l to its layer container. An element of this * Vector is an Integer.. */ public void setLayer(Vector<Integer> l) { this.layer = l; } /** * Returns my priority. The layer is used by grammar with rule priority. */ public Vector<Integer> getPriority() { return this.priority; } public String getPriorityAsString() { String str = ""; for (int k = 0; k < this.priority.size(); k++) { int l = this.priority.get(k).intValue(); str = str + String.valueOf(l); if (k < this.priority.size() - 1) str = str + ","; } return str; } /** * Add the specified priority p to its priority container. */ public void addPriority(int p) { boolean added = false; for (int i = 0; i < this.priority.size(); i++) { if (p <= this.priority.get(i).intValue()) { this.priority.add(i, new Integer(p)); added = true; break; } } if (!added) this.priority.add(new Integer(p)); } /** * Set the specified Vector p to its priority container. An element of this * Vector is an Integer. */ public void setPriority(Vector<Integer> p) { this.priority = p; } @SuppressWarnings("unused") public void XreadObject(XMLHelper h) { if (h.isTag("Formula", this)) { this.name = h.readAttr("name"); String str = h.readAttr("comment"); if (!str.equals("")) this.comment = str; Object attr_enabled = h.readAttr("enabled"); if ((attr_enabled != null) && ((String) attr_enabled).equals("false")) this.enabled = false; else this.enabled = true; if (this.layer == null) this.layer = new Vector<Integer>(5); else this.layer.clear(); // read layer if (h.readSubTag("Layer")) { String l = h.readAttr("Layer"); String size = h.readAttr("Size"); if (!"".equals(l)) { l = l.replaceAll(" ", ""); if (l.indexOf("[") >= 0) l = l.substring(1); if (l.indexOf("]") >= 0) l = l.substring(0, l.length()-1); String[] array = l.split(","); for (int i = 0; i < array.length; i++) { try { this.layer.add(new Integer(array[i])); } catch(java.lang.NumberFormatException ex) {} } } h.close(); } if (this.priority == null) this.priority = new Vector<Integer>(5); else this.priority.clear(); // read priority if (h.readSubTag("Priority")) { String p = h.readAttr("Priority"); String size = h.readAttr("Size"); if (!"".equals(p)) { p = p.replaceAll(" ", ""); if (p.indexOf("[") >= 0) p = p.substring(1); if (p.indexOf("]") >= 0) p = p.substring(0, p.length()-1); String[] array = p.split(","); for (int i = 0; i < array.length; i++) { try { this.priority.add(new Integer(array[i])); } catch(java.lang.NumberFormatException ex) {} } } h.close(); } h.close(); } } public void XwriteObject(XMLHelper h) { h.openNewElem("Formula", this); h.addAttr("name", getName()); h.addAttr("comment", this.comment); h.addAttr("enabled", String.valueOf(this.enabled)); // write layer h.openSubTag("Layer"); if (this.layer.size() == 0) { h.addAttr("Layer", ""); } else if (this.layer.size() == 1) { String l = this.layer.get(0).toString(); h.addAttr("Layer", l); } else { h.addAttr("Layer", this.layer.toString()); } h.addAttr("Size", String.valueOf(this.layer.size())); h.close(); // write priority h.openSubTag("Priority"); if (this.priority.size() == 0) { h.addAttr("Priority", ""); } else if (this.priority.size() == 1) { String p = this.priority.get(0).toString(); h.addAttr("Priority", p); } else { h.addAttr("Priority", this.priority.toString()); } h.addAttr("Size", String.valueOf(this.priority.size())); h.close(); h.close(); } /* (non-Javadoc) * @see agg.cons.Evaluable#evalForall(java.lang.Object, int, boolean) */ public boolean evalForall(Object o, int t) { int tick = t; if (tick == -1) { if (this.running_tick < 0) this.running_tick = 0; tick = this.running_tick++; } switch (this.op) { case NOP: if (this.sub1 != null) { if (this.sub1 instanceof NestedApplCond) { ((NestedApplCond) this.sub1).forall = true; } boolean result = this.sub1.eval(o, tick); setEvaluable(this.sub1); if (this.sub1 instanceof NestedApplCond) { ((NestedApplCond) this.sub1).forall = false; } return result; } return false; } return false; } /** * Trims the capacity of used vectors to be the vector's current * size. */ public void trimToSize() { this.layer.trimToSize(); this.priority.trimToSize(); } }