package context.arch.enactor; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.jdom.Document; import org.jdom.Element; import org.jdom.Namespace; import context.arch.discoverer.ComponentDescription; import context.arch.discoverer.ComponentDescriptions; import context.arch.discoverer.query.AbstractQueryItem; import context.arch.service.helper.ServiceInput; import context.arch.storage.Attribute; import context.arch.storage.Attributes; import context.arch.widget.Widget; import context.arch.widget.WidgetXmlParser; /** * Utility class to generate rule-based Enactors from XML declarations. * * @author Brian Y. Lim * @apiviz.uses context.arch.enactor.Enactor * @see WidgetXmlParser */ public class EnactorXmlParser { /** * Stub class to generate rule-based Enactors from XML declarations. * @author Brian Y. Lim * */ private static class EnactorXml { private URL baseUrl; private Element rootNode; private String enactorId; /** * Create an Enactor stub defined in an XML file. * @param filename * @param enactorId */ public EnactorXml(String filename, String enactorId) { Document document = WidgetXmlParser.getDocument(filename); rootNode = document.getRootElement(); // <Enactor> this.enactorId = enactorId; try { baseUrl = new File(filename).getParentFile().toURI().toURL(); // for relative HREF } catch (MalformedURLException e) { e.printStackTrace(); } } /** * @return the enactor Id in URL encoded form. */ private String getFullId() { return WidgetXmlParser.urlEncode(enactorId); } } /* ------------------------------------------------------------------------------ */ /** * Very basic enactor with no constant attribute values of in and out widgets set. * Id set as timestamp of creation. */ public static Enactor createEnactor(String filename) { return createEnactor(filename, String.valueOf(System.currentTimeMillis()), new Attributes(), // empty new Attributes() ); } /** * Convenience method to get an enactor with minimal arguments. * Id of enactor is automatically set by appending the Ids of inWidget and outWidget * @param filename of XML file containing enactor definition * @param inWidget to extract constant attribute values from, regarding in-widget * @param outWidget to extract constant attribute values from, regarding out-widget * @return Enactor specified in XML file. */ public static Enactor createEnactor(String filename, Widget inWidget, Widget outWidget) { String enactorId = inWidget.getId() + "_" + outWidget.getId(); return createEnactor(new EnactorXml(filename, enactorId), inWidget.getConstantAttributes(), outWidget.getConstantAttributes()); } /** * Convenience method to get an enactor with minimal arguments. * @param filename of XML file containing enactor definition * @param enactorId to set enactor to * @param inConstAttValues constant attribute values regarding in-widget * @param outConstAttValues constant attribute values regarding out-widget * @return Enactor specified in XML file. */ public static Enactor createEnactor(String filename, String enactorId, Attributes inConstAtts, Attributes outConstAtts) { return createEnactor(new EnactorXml(filename, enactorId), inConstAtts, outConstAtts); } private static Enactor createEnactor(final EnactorXml exml, Attributes inConstAtts, Attributes outConstAtts) { final Namespace ns = exml.rootNode.getNamespace(); final String enactorName = exml.rootNode.getAttributeValue("name"); try { final ComponentDescriptions inWidgetStubs = new ComponentDescriptions(); final ComponentDescriptions outWidgetStubs = new ComponentDescriptions(); List<AbstractQueryItem<?, ?>> inWidgetQueries = new ArrayList<AbstractQueryItem<?, ?>>(); List<AbstractQueryItem<?, ?>> outWidgetQueries = new ArrayList<AbstractQueryItem<?, ?>>(); /* * get stubs in widgets */ for (Object refChild : exml.rootNode.getChildren("InWidget", ns)) { Element refElement = (Element) refChild; String inHref = refElement.getAttributeValue("href"); ComponentDescription inWidgetStub = WidgetXmlParser.createWidgetStub(new URL(exml.baseUrl, inHref), "", inConstAtts); // doesn't set id inWidgetStubs.add(inWidgetStub); /* * extract subscription queries for widgets */ AbstractQueryItem<?, ?> inWidgetQuery = WidgetXmlParser.createWidgetSubscriptionQuery(inWidgetStub); inWidgetQueries.add(inWidgetQuery); } /* * get stubs out widgets */ for (Object refChild : exml.rootNode.getChildren("OutWidget", ns)) { Element refElement = (Element) refChild; String outHref = refElement.getAttributeValue("href"); ComponentDescription outWidgetStub = WidgetXmlParser.createWidgetStub(new URL(exml.baseUrl, outHref), "", outConstAtts); outWidgetStubs.add(outWidgetStub); /* * extract subscription queries for widgets */ AbstractQueryItem<?, ?> outWidgetQuery = WidgetXmlParser.createWidgetSubscriptionQuery(outWidgetStub); outWidgetQueries.add(outWidgetQuery); } // get outcome name String outcomeName = exml.rootNode.getChildText("OutcomeName", ns); /* * parse and store constant vars used in the XML notation */ final Map<String, Comparable<?>> constVars = new HashMap<String, Comparable<?>>(); // <name, stringValue> for (Object child : exml.rootNode.getChildren("const", ns)) { Element constElement = (Element) child; String name = constElement.getAttributeValue("name"); try { Class<?> constType = WidgetXmlParser.toClass(constElement.getAttributeValue("type")); String strValue = constElement.getText(); Comparable<?> value = (Comparable<?>) constType.getMethod("valueOf", String.class) .invoke(null, strValue); constVars.put(name, value); } catch (Exception e) { e.printStackTrace(); } } /* * construct enactor with details for EnactorReferences in its constructor */ Enactor enactor = new Enactor(inWidgetQueries.toArray(new AbstractQueryItem<?,?>[0]), outWidgetQueries.toArray(new AbstractQueryItem<?,?>[0]), outcomeName, exml.getFullId()) { { // Constructor // to store queries by name to be reusable (e.g. for ElseQueryItem) Map<String, AbstractQueryItem<?, ?>> queries = new HashMap<String, AbstractQueryItem<?,?>>(); // iterate enactor references for (Object refChild : exml.rootNode.getChildren("Reference", ns)) { Element refElement = (Element) refChild; String refName = refElement.getAttributeValue("name"); /* * Text for query */ Element queryElement = refElement.getChild("Query", ns); String queryName = queryElement.getAttributeValue("name"); String queryStr = queryElement.getText().trim(); // parse query into Abstract Syntax Tree QueryParser parser = new QueryParser(queryStr, constVars, queries, inWidgetStubs); AbstractQueryItem<?, ?> query = parser.parseQuery(); queries.put(queryName, query); // query may be referenced, so store it /* * Parse outcome assignments * Can have multiple */ List<AttributeEvalParser<?>> assnParsers = new ArrayList<AttributeEvalParser<?>>(); for (Object outcomeChild : refElement.getChildren("Outcome", ns)) { Element outcomeElement = (Element) outcomeChild; String outAttName = outcomeElement.getAttributeValue("outAttribute"); Attribute<?> outAtt = outWidgetStubs.mergeComponentDescriptions().getNonConstantAttribute(outAttName); String assnStr = outcomeElement.getText().trim(); // parse assignment AttributeEvalParser<?> assnParser = AttributeEvalParser.instance(outAtt, assnStr, constVars); assnParsers.add(assnParser); } /* * ServiceInputs to trigger with enactor reference. * May have more than one */ List<ServiceInput> serviceInputs = new ArrayList<ServiceInput>(); for (Object serviceChild : refElement.getChildren("ServiceInput", ns)) { Element serviceElement = (Element) serviceChild; String serviceName = serviceElement.getAttributeValue("service"); String functionName = serviceElement.getAttributeValue("function"); // get attributes Attributes allAtts = outWidgetStubs.mergeComponentDescriptions().getAllAttributes(); Attributes inputAtts = new Attributes(); for (Object attChild : serviceElement.getChildren("Attribute", ns)) { Element attElement = (Element) attChild; String attName = attElement.getAttributeValue("name"); // get attribute from outWidget's non-constant attributes Attribute<?> att = Attribute.instance( attName, allAtts.get(attName).getType()); inputAtts.add(att); } ServiceInput input = new ServiceInput(serviceName, functionName, inputAtts); serviceInputs.add(input); } /* * Create enactor reference with accumulated parameters */ EnactorReference er = new EnactorReference(this, query, refName, assnParsers, serviceInputs); addReference(er); } } @Override public String getClassname() { return enactorName; } }; // start the enactor enactor.start(); return enactor; } catch (MalformedURLException e) { e.printStackTrace(); } return null; } /* ------------------------------------------------------------------------------ */ /** * Very basic generator with no constant attribute values of out widget set. * Id set as timestamp of creation. */ public static Generator createGenerator(String filename) { return createGenerator( new EnactorXml( filename, String.valueOf(System.currentTimeMillis())), new Attributes() // empty ); } public static Generator createGenerator(String filename, Widget outWidget) { String enactorId = "_" + outWidget.getId(); EnactorXml exml = new EnactorXml(filename, enactorId); return createGenerator(exml, outWidget.getConstantAttributes()); } private static Generator createGenerator(final EnactorXml exml, Attributes outConstAtts) { final Namespace ns = exml.rootNode.getNamespace(); final String enactorName = exml.rootNode.getAttributeValue("name"); try { /* * get stubs for out widget */ String outHref = exml.rootNode.getChild("OutWidget", ns).getAttributeValue("href"); final ComponentDescription outWidgetStub = WidgetXmlParser.createWidgetStub(new URL(exml.baseUrl, outHref), "", outConstAtts); /* * extract subscription query for widget */ AbstractQueryItem<?, ?> outWidgetQuery = WidgetXmlParser.createWidgetSubscriptionQuery(outWidgetStub); // get outcome name String outcomeName = exml.rootNode.getChildText("OutcomeName", ns); /* * parse and store constant vars used in the XML notation */ final Map<String, Object> constVars = new HashMap<String, Object>(); // <name, stringValue> for (Object child : exml.rootNode.getChildren("const", ns)) { Element constElement = (Element) child; String name = constElement.getAttributeValue("name"); try { Class<?> constType = WidgetXmlParser.toClass(constElement.getAttributeValue("type")); String strValue = constElement.getText(); Object value = constType.getMethod("valueOf", String.class) .invoke(null, strValue); constVars.put(name, value); } catch (Exception e) { e.printStackTrace(); } } /* * construct generator with details for EnactorReferences in its constructor */ Generator generator = new Generator(outWidgetQuery, outcomeName, exml.getFullId()) { @Override public String getClassname() { return enactorName; } }; return generator; } catch (MalformedURLException e) { e.printStackTrace(); } return null; } }