package context.arch.intelligibility.rules; import context.arch.discoverer.component.AbstractElement; import context.arch.discoverer.component.AttributeElement; import context.arch.discoverer.component.CallbackElement; import context.arch.discoverer.component.ClassnameElement; import context.arch.discoverer.component.HostnameElement; import context.arch.discoverer.component.IdElement; import context.arch.discoverer.component.NonConstantAttributeElement; import context.arch.discoverer.component.PortElement; import context.arch.discoverer.component.ServiceElement; import context.arch.discoverer.component.SubscriberElement; import context.arch.discoverer.component.TypeElement; import context.arch.discoverer.query.ANDQueryItem; import context.arch.discoverer.query.AbstractQueryItem; import context.arch.discoverer.query.ElseQueryItem; import context.arch.discoverer.query.NOTQueryItem; import context.arch.discoverer.query.ORQueryItem; import context.arch.discoverer.query.RuleQueryItem; import context.arch.discoverer.query.comparison.AbstractComparison; import context.arch.discoverer.query.comparison.AttributeComparison; import context.arch.enactor.Enactor; import context.arch.enactor.EnactorXmlParser; import context.arch.intelligibility.expression.Comparison; import context.arch.intelligibility.expression.Conjunction; import context.arch.intelligibility.expression.DNF; import context.arch.intelligibility.expression.Disjunction; import context.arch.intelligibility.expression.Expression; import context.arch.intelligibility.expression.Negation; import context.arch.intelligibility.expression.Parameter; import context.arch.storage.AttributeNameValue; import context.arch.storage.Attributes; import context.arch.widget.WidgetXmlParser; /** * Parses the AbstractQueryItem abstract syntax tree (AST) into the Expression tree, and into DNF form. * @author Brian Y. Lim * */ public class QueryItemParser { /** * Converts a query into an Expression equivalent, and also in DNF form. * Rules can have multiple traces, but all have the same output. * Traces are a disjunctions of trace conjunctions. * Each trace is a conjunction of Comparisons * @param query */ public static DNF parse(AbstractQueryItem<?,?> query) { Expression ruleTree = parseRecurse(query); DNF dnf = DNF.toDNF(ruleTree); return dnf; } /** * Converts a query into an Expression equivalent. Would do this recursively if query consists of boolean/list queries with children. * * Subclasses should override and extend this method to be able to attach expressions for newly defined AbstractQueryItems. * * @param query * @param trace one trace per leaf, create new traces by duplicating from parent * @param traces */ private static Expression parseRecurse(AbstractQueryItem<?,?> query) { /* * Atomic rule queries */ if (query instanceof RuleQueryItem<?,?>) { return parse((RuleQueryItem<?,?>) query); } /* * Boolean queries */ else if (query instanceof ANDQueryItem) { return parseRecurse((ANDQueryItem) query); } else if (query instanceof ORQueryItem) { return parseRecurse((ORQueryItem) query); } else if (query instanceof NOTQueryItem) { return parseRecurse((NOTQueryItem) query); } else if (query instanceof ElseQueryItem) { return parseRecurse((ElseQueryItem) query); } else { return null; // TODO: we get null, because we are using custom QueryItems and custom Comparisons } } @SuppressWarnings("unchecked") private static <T extends Comparable<? super T>> Parameter<T> parse(RuleQueryItem<?,?> query) { RuleQueryItem<?,?> qi = (RuleQueryItem<?,?>)query; AbstractComparison<?,?> comp = qi.getComparison(); AbstractElement<?,?,?> elementToMatch = qi.getElementToMatch(); //String relationship = comp.getComparisonName(); /* * Match element about Attribute */ if (elementToMatch instanceof AttributeElement) { AttributeNameValue<T> att = (AttributeNameValue<T>) ((AttributeElement)elementToMatch).getValue(); String attName = att.getName(); T value = att.getValue(); /* * Would only compare attribute values as Comparable (EQUAL, GREATER, etc) * So can map directly to context.arch.intelligibility.expression.Comparison */ if (comp instanceof AttributeComparison) { // convert comparison AttributeComparison attrComp = (AttributeComparison) comp; Comparison.Relation relation; switch (attrComp.getComparison()) { case EQUAL: relation = Comparison.Relation.EQUALS; break; case DIFFERENT: relation = Comparison.Relation.NOT_EQUALS; break; case GREATER: relation = Comparison.Relation.GREATER_THAN; break; case GREATER_EQUAL: relation = Comparison.Relation.GREATER_THAN_OR_EQUAL; break; case LESS: relation = Comparison.Relation.LESS_THAN; break; case LESS_EQUAL: relation = Comparison.Relation.LESS_THAN_OR_EQUAL; break; default: relation = Comparison.Relation.NO_RELATION; break; } if (att.isNumeric()) { return Comparison.instance(attName, value, relation); } else { return Parameter.instance(attName, value); } } else { throw new RuntimeException("some other form of comparison for Attribute"); // TODO support some other form of comparison for Attribute } } /* * For matching other elements of ComponentDescription. * Assumes that it is just an equality match. * TODO: generalize for other types of comparison */ String paramName = elementToMatch.getElementName(); T paramValue = (T) elementToMatch.getValue(); if ( elementToMatch instanceof IdElement || elementToMatch instanceof HostnameElement || elementToMatch instanceof PortElement || elementToMatch instanceof ClassnameElement || elementToMatch instanceof TypeElement || elementToMatch instanceof CallbackElement || elementToMatch instanceof ServiceElement || elementToMatch instanceof SubscriberElement ) { return Parameter.instance(paramName, paramValue); } return null; } private static Conjunction<Expression> parseRecurse(ANDQueryItem query) { Conjunction<Expression> list = new Conjunction<Expression>(); for (AbstractQueryItem<?,?> child : query.getChildren()) { list.add(parseRecurse(child)); } return list; } private static Disjunction<Expression> parseRecurse(ORQueryItem query) { Disjunction<Expression> list = new Disjunction<Expression>(); for (AbstractQueryItem<?,?> child : query.getChildren()) { list.add(parseRecurse(child)); } return list; } private static Expression parseRecurse(NOTQueryItem query) { Expression child = parseRecurse(query.getChild()); return Negation.negate(child); } private static Expression parseRecurse(ElseQueryItem query) { Disjunction<Expression> list = new Disjunction<Expression>(); for (AbstractQueryItem<?,?> child : query.getChildren()) { list.add(parseRecurse(child)); } Expression ret = Negation.negate(list); return ret; } @SuppressWarnings("serial") public static void main(String[] args) { AbstractQueryItem<?,?> query = WidgetXmlParser.createWidgetSubscriptionQuery( "demos/room-rules/room-widget.xml", "Living Room", // widgetId new Attributes()); // no constant attribute values to set query = RuleQueryItem.instance( new NonConstantAttributeElement(AttributeNameValue.instance("greater_test", 10)), AttributeComparison.GREATER ); Enactor enactor = EnactorXmlParser.createEnactor( "demos/room-rules/room-enactor.xml", "Living Room Ceiling", new Attributes() {{ addAttribute("room", "Living Room"); }}, new Attributes() {{ addAttribute("lamp", "Ceiling"); }}); // query = enactor.getReference("Off").getConditionQuery(); // Disjunction dnf = QueryItemParser.parse(query); // System.out.println("dnf = " + dnf); query = enactor.getReference("On").getConditionQuery(); DNF dnf = QueryItemParser.parse(query); System.out.println("dnf = " + dnf); } }