package net.contextfw.web.commons.js;
import java.util.List;
import java.util.Locale;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Text;
import com.google.inject.Singleton;
/**
* Provides means to read templates from XSL and transform them into Javascript.
*
* <p>
* This class provides means to read specific templates from XSL-document and
* produce a set of javascript functions that contain the information of the
* templates.
* </p>
*
* <p>
* Note! At this point JsTemplate is considered experimental.
* </p>
*
* @since 0.8.1
*/
@Singleton
class JsTemplateServiceImpl implements JsTemplateService {
private final JsTemplateServlet servlet;
public JsTemplateServiceImpl(JsTemplateServlet servlet) {
this.servlet = servlet;
}
@SuppressWarnings("unchecked")
private String generateTemplates(Document document) {
List<Element> nodes = document.getRootElement().selectNodes("//js:template");
String value = handleJSElements(nodes);
for (Element el : nodes) {
el.detach();
}
return value;
}
private String handleJSElements(List<Element> elements) {
StringBuilder b = new StringBuilder();
for (Element element : elements) {
b.append(element.attributeValue("declaration")).append("{\n");
StringBuilder buffer = new StringBuilder();
b.append("var b = [];\n");
for (Object obj : element.content()) {
traverse(b, buffer, obj);
}
flushBuffer(b, buffer);
b.append("return b.join('');\n");
b.append("}\n");
}
return b.toString();
}
private void traverse(StringBuilder b, StringBuilder buffer, Object obj) {
if (obj instanceof Text) {
buffer.append(((Text) obj).getText());
} else if (obj instanceof Element) {
traverseEl(b, buffer, (Element) obj);
} else {
b.append(obj.getClass().getSimpleName()).append("\n");
}
}
@SuppressWarnings("unchecked")
private void traverseEl(StringBuilder b, StringBuilder buffer, Element element) {
String name = element.getName();
String prefix = element.getNamespacePrefix();
if ("js".equals(prefix)) {
flushBuffer(b, buffer);
if ("script".equals(name)) {
b.append(StringUtils.trimToEmpty(element.getText()) + "\n");
} else if ("var".equals(name)) {
b.append("b.push(" + element.attributeValue("name") + ");\n");
}
} else {
buffer.append("<").append(name);
addAttributes(b, buffer, element.attributes());
String lName = name.toLowerCase(Locale.ENGLISH);
if ("area".equals(lName) ||
"base".equals(lName) ||
"basefont".equals(lName) ||
"br".equals(lName) ||
"col".equals(lName) ||
"frame".equals(lName) ||
"hr".equals(lName) ||
"img".equals(lName) ||
"input".equals(lName) ||
"link".equals(lName) ||
"meta".equals(lName) ||
"param".equals(lName)) {
buffer.append("/>");
} else {
buffer.append(">");
for (Object obj : element.content()) {
traverse(b, buffer, obj);
}
buffer.append("</" + name + ">");
}
}
}
private void flushBuffer(StringBuilder b, StringBuilder buffer) {
b.append("b.push('" + StringEscapeUtils.escapeJavaScript(buffer.toString()) + "');\n");
buffer.setLength(0);
}
private void addAttributes(StringBuilder b, StringBuilder buffer, List<Attribute> attrs) {
if (attrs.size() > 0) {
for (Attribute attr : attrs) {
if (attr.getNamespacePrefix().equals("js")) {
flushBuffer(b, buffer);
b.append("if (" + attr.getValue() + " != undefined) b.push(' " + attr.getName()
+ "=\"'+" + attr.getValue() + "+'\"');\n");
} else {
buffer.append(" " + attr.getName() + "=\"" + attr.getValue() + "\"");
}
}
}
}
@Override
public void process(Document document) {
servlet.setContent(generateTemplates(document));
}
}