private String processMacroToString(Environment env, String widgetName, Macro macro, Map<String, Object> map) { StringWriter out = new StringWriter(); try { String templateString = macro.getChildNodes().get(0).toString(); // NB Using this method of creating a template from a string does not allow the widget template to import // other templates (but it can include other templates). We'd need to use a StringTemplateLoader // in the config instead. See StringTemplateLoader API doc. // The problem is that the StringTemplateLoader has to be added to the config's MultiTemplateLoader. // Then to support multi-threading, we can't just add the widget here to the StringTemplateLoader with // the same key, e.g., "widgetTemplate", since one putTemplate() call will clobber a previous one. // We need to give each widget macro template a unique key in the StringTemplateLoader, and check // if it's already there or else add it. Leave this for later. Template template = new Template("widget", new StringReader(templateString), env.getConfiguration()); template.process(map, out); } catch (Exception e) { log.error("Could not process widget " + widgetName, e); } String output = out.toString(); log.debug("Macro output: " + output); return output; }