package org.caudexorigo.jpt;
import java.io.IOException;
import java.net.URI;
import java.net.URLDecoder;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;
import nu.xom.Attribute;
import nu.xom.Document;
import nu.xom.Element;
import nu.xom.Node;
import nu.xom.ParsingException;
import nu.xom.ValidityException;
import org.apache.commons.lang3.StringUtils;
import org.caudexorigo.xom.XomDocumentBuilder;
import org.caudexorigo.xom.XomUtils;
public class JptNodeBuilder extends BaseJptNodeBuilder
{
private static final String METAL_NS = "http://xml.zope.org/namespaces/metal";
private static final String TAL_NS = "http://xml.zope.org/namespaces/tal";
private static final String APPEND_SLOT_NS = "http://xml.zope.org/namespaces/metal/append-to-slot";
private URI _templateUri;
private JptDocument _jptDocument;
private StringBuilder _sb;
private ArrayList<Element> _slotFillerList = new ArrayList<Element>();
private ArrayList<Element> _slotAppenderList = new ArrayList<Element>();
private ArrayList<Dependency> _dependecies = new ArrayList<Dependency>();
private Deque<JptParentNode> pnodes = new ArrayDeque<JptParentNode>();
private Map<String, Element> slotFillers = new HashMap<String, Element>();
private Map<String, Element> slotAppenders = new HashMap<String, Element>();
private boolean _isInSlot = false;;
public JptNodeBuilder()
{
this(new StringBuilder());
}
private JptNodeBuilder(StringBuilder sb)
{
super(sb);
_sb = sb;
}
public JptDocument getJptDocument()
{
return _jptDocument;
}
private void prepareElement(Element el) throws ValidityException, ParsingException, IOException
{
if (XomUtils.getAttribute(el, "metal:use-macro") != null)
{
processMetalUseMacro(el);
return;
}
if (XomUtils.getAttribute(el, "metal:define-slot") != null)
{
processMetalDefineSlot(el);
return;
}
if (XomUtils.getAttribute(el, "tal:condition") != null)
{
processTalCondition(el);
return;
}
if (XomUtils.getAttribute(el, "tal:repeat") != null)
{
processTalRepeat(el);
return;
}
if (XomUtils.getAttribute(el, "tal:content") != null)
{
processTalContent(el);
return;
}
if (XomUtils.getAttribute(el, "tal:replace") != null)
{
processTalReplace(el);
return;
}
if (XomUtils.getAttribute(el, "tal:include") != null)
{
processTalInclude(el);
return;
}
if (XomUtils.getAttribute(el, "tal:omit-tag") != null)
{
processTalOmitTag(el);
return;
}
if ("template".equals(el.getLocalName()) && TAL_NS.equals(el.getNamespaceURI()))
{
processTalTemplate(el);
return;
}
if (XomUtils.getAttribute(el, "metal:fill-slot") != null)
el.removeAttribute(XomUtils.getAttribute(el, "metal:fill-slot"));
if (XomUtils.getAttribute(el, "metal:define-macro") != null)
el.removeAttribute(XomUtils.getAttribute(el, "metal:define-macro"));
super.process(el);
}
private void processTalTemplate(Element el)
{
addStaticFragments();
StringBuilder template = new StringBuilder();
for (int i = 0; i < el.getChildCount(); i++)
{
template.append(el.getChild(i).toXML());
}
el.removeChildren();
JptTemplateNode jout;
if (el.getBaseURI().equals(APPEND_SLOT_NS))
{
jout = new JptTemplateNode(template.toString(), true);
}
else
{
jout = new JptTemplateNode(template.toString(), _isInSlot);
}
removePNodeChildren();
((JptParentNode) pnodes.peek()).appendChild(jout);
}
private void processTalInclude(Element el)
{
Attribute attribute = XomUtils.getAttribute(el, "tal:include");
if (attribute != null)
{
el.removeAttribute(attribute);
addStaticFragments();
el.removeChildren();
JptIncludeNode jinclude = new JptIncludeNode(attribute.getValue(), _isInSlot);
removePNodeChildren();
((JptParentNode) pnodes.peek()).appendChild(jinclude);
}
}
public void process(Document doc, URI templateUri)
{
_templateUri = templateUri;
findSlotActorsInDocument(doc);
prepareSlotsActorsInDocument();
String object_class_name = ContextBuilder.objectNameFromInstructions(doc);
_jptDocument = new JptDocument(object_class_name);
pnodes.push(_jptDocument);
super.process(doc);
JptStaticFragment sf = new JptStaticFragment(_sb.toString());
((JptParentNode) pnodes.peek()).appendChild(sf);
}
private Map<String, String> extractMacroParams(String uri_qs)
{
try
{
Map<String, String> query_pairs = new HashMap<String, String>();
String[] pairs = uri_qs.split("&");
for (String pair : pairs)
{
int idx = pair.indexOf("=");
query_pairs.put(URLDecoder.decode(pair.substring(0, idx), "UTF-8"), URLDecoder.decode(pair.substring(idx + 1), "UTF-8"));
}
return query_pairs;
}
catch (Throwable t)
{
throw new RuntimeException(t);
}
}
private void findSlotActorsInDocument(Node node)
{
for (int i = 0; i < node.getChildCount(); i++)
{
if (node.getChild(i) instanceof Element)
{
Element element = (Element) node.getChild(i);
String slot_filler_name = element.getAttributeValue("fill-slot", METAL_NS);
if (slot_filler_name != null)
_slotFillerList.add(element);
String slot_to_append_name = element.getAttributeValue("append-to-slot", METAL_NS);
if (slot_to_append_name != null)
_slotAppenderList.add(element);
}
findSlotActorsInDocument(node.getChild(i));
}
}
private void prepareSlotsActorsInDocument()
{
Element slot_filler_elements[] = (Element[]) _slotFillerList.toArray(new Element[0]);
for (int i = 0; i < slot_filler_elements.length; i++)
{
Element element = slot_filler_elements[i];
String slot_to_fill_name = element.getAttributeValue("fill-slot", METAL_NS);
slotFillers.put(slot_to_fill_name, (Element) element.copy());
}
Element slot_appender_elements[] = (Element[]) _slotAppenderList.toArray(new Element[0]);
for (int i = 0; i < slot_appender_elements.length; i++)
{
Element element = slot_appender_elements[i];
String slot_to_fill_name = element.getAttributeValue("append-to-slot", METAL_NS);
slotAppenders.put(slot_to_fill_name, (Element) element.copy());
}
}
private void prepareMacro(URI muri, Document macroDocument, Node useMacroNode, String macroPath, Map<String, String> macroParams) throws ValidityException, ParsingException, IOException
{
String macroName = macroParams.get("macro");
// System.out.println("JptNodeBuilder.prepareMacro.macroName: " + macroName);
// System.out.println("JptNodeBuilder.prepareMacro.macroPath: " + macroPath);
// System.out.println("JptNodeBuilder.prepareMacro.isInSlot: " + _isInSlot);
Document macro_doc = macroDocument.getDocument();
findSlotActorsInDocument(macroDocument);
prepareSlotsActorsInDocument();
Element[] defined_macro = new Element[1];
XomUtils.findSpecificMacro(macro_doc, macroName, defined_macro);
Element subtree = defined_macro[0];
if (subtree == null)
{
throw new IllegalArgumentException(String.format("could not find macro '%s' in template '%s'", macroName, macroPath));
}
String object_class_name = ContextBuilder.objectNameFromInstructions(macro_doc);
JptMacroNode jpt_macro_node = new JptMacroNode(muri, object_class_name, _isInSlot, macroParams);
addStaticFragments();
pnodes.push(jpt_macro_node);
_isInSlot = false;
prepareElement(subtree);
pnodes.pop();
((JptParentNode) pnodes.peek()).appendChild(jpt_macro_node);
}
@Override
protected void process(Element element)
{
try
{
prepareElement(element);
}
catch (ValidityException e)
{
e.printStackTrace();
}
catch (ParsingException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
}
protected void processAttributes(Element element)
{
processTalAttributes(element);
super.processAttributes(element);
}
private void processTalAttributes(Element el)
{
processTalConditionalAttributes(el);
Attribute attribute = XomUtils.getAttribute(el, "tal:attributes");
if (attribute != null)
{
el.removeAttribute(attribute);
Attribute tal_attributes[] = XomUtils.processTalAttributes(attribute);
Attribute aattribute[] = tal_attributes;
int i = 0;
for (int j = aattribute.length; i < j; i++)
{
Attribute tal_attribute = aattribute[i];
Attribute attr_to_remove = el.getAttribute(tal_attribute.getLocalName());
if (attr_to_remove != null)
el.removeAttribute(attr_to_remove);
}
addStaticFragments();
Attribute aattribute1[] = tal_attributes;
int k = 0;
for (int l = aattribute1.length; k < l; k++)
{
Attribute tal_attribute = aattribute1[k];
JptAttributeNode jattr = new JptAttributeNode(tal_attribute, _isInSlot);
((JptParentNode) pnodes.peek()).appendChild(jattr);
}
}
}
private void processTalConditionalAttributes(Element el)
{
Attribute attribute = XomUtils.getAttribute(el, "tal:conditionalAttributes");
if (attribute != null)
{
el.removeAttribute(attribute);
Attribute tal_attributes[] = XomUtils.processTalAttributes(attribute);
Attribute aattribute[] = tal_attributes;
int i = 0;
for (int j = aattribute.length; i < j; i++)
{
Attribute tal_attribute = aattribute[i];
Attribute attr_to_remove = el.getAttribute(tal_attribute.getLocalName());
if (attr_to_remove != null)
el.removeAttribute(attr_to_remove);
}
addStaticFragments();
Attribute aattribute1[] = tal_attributes;
int k = 0;
for (int l = aattribute1.length; k < l; k++)
{
Attribute tal_attribute = aattribute1[k];
JptConditionalAttributeNode jattr = new JptConditionalAttributeNode(tal_attribute, _isInSlot);
((JptParentNode) pnodes.peek()).appendChild(jattr);
}
}
}
private void processTalCondition(Element el)
{
Attribute attribute = XomUtils.getAttribute(el, "tal:condition");
if (attribute != null)
{
String current_attribute_value = attribute.getValue();
// System.out.println("JptNodeBuilder.processTalCondition.el: " + el.toXML());
// System.out.println("JptNodeBuilder.processTalCondition.expression: " + current_attribute_value);
// System.out.println("JptNodeBuilder.processTalCondition.sb: »»»»" + _sb.toString() + "«««");
addStaticFragments();
el.removeAttribute(attribute);
JptConditionalNode jcond = new JptConditionalNode(current_attribute_value, _isInSlot);
pnodes.push(jcond);
process(el);
addStaticFragments();
// processTalRepeat(el);
// processTalReplace(el);
// processTalContent(el);
pnodes.pop();
((JptParentNode) pnodes.peek()).appendChild(jcond);
}
}
private void processTalOmitTag(Element el)
{
Attribute omit_tag_attr = XomUtils.getAttribute(el, "tal:omit-tag");
if (omit_tag_attr != null)
{
// System.out.println("JptNodeBuilder.processTalOmitTag.parent.begin: " + (((JptParentNode) pnodes.peek())).getClass().getCanonicalName());
// System.out.println("JptNodeBuilder.processTalOmitTag.el: " + el.toXML());
el.removeAttribute(omit_tag_attr);
addStaticFragments();
String omit_tag_condition = StringUtils.isBlank(omit_tag_attr.getValue()) ? "false" : String.format("!%s", omit_tag_attr.getValue());// conditional nodes output on true, revert the logic, should not output on true
JptHolderNode holder_node = new JptHolderNode(_isInSlot);
((JptParentNode) pnodes.peek()).appendChild(holder_node);
pnodes.push(holder_node);
JptConditionalNode start_cond = new JptConditionalNode(omit_tag_condition, _isInSlot);
pnodes.push(start_cond);
processStartTag(el);
addStaticFragments();
pnodes.pop(); // remove conditional node
((JptParentNode) pnodes.peek()).appendChild(start_cond);
int child_count = el.getChildCount();
for (int i = 0; i < child_count; i++)
{
Node c = el.getChild(i);
processChild(c);
}
addStaticFragments();
JptConditionalNode end_cond = new JptConditionalNode(omit_tag_condition, _isInSlot);
pnodes.push(end_cond);
processEndTag(el);
addStaticFragments();
pnodes.pop(); // remove conditional node
((JptParentNode) pnodes.peek()).appendChild(end_cond);
pnodes.pop(); // remove holder node
// System.out.println("JptNodeBuilder.processTalOmitTag.parent.end: " + (((JptParentNode) pnodes.peek())).getClass().getCanonicalName());
}
else
{
process(el);
}
}
private void addStaticFragments()
{
JptStaticFragment sf = new JptStaticFragment(_sb.toString());
JptParentNode parent = ((JptParentNode) pnodes.peek());
parent.appendChild(sf);
_sb.delete(0, _sb.length());
}
private void processTalContent(Element el)
{
Attribute attribute = XomUtils.getAttribute(el, "tal:content");
if (attribute != null)
{
el.removeAttribute(attribute);
addStaticFragments();
Attribute omit_tag_attr = XomUtils.getAttribute(el, "tal:omit-tag");
if (omit_tag_attr != null)
{
el.removeAttribute(omit_tag_attr);
String omit_tag_condition = StringUtils.isBlank(omit_tag_attr.getValue()) ? "false" : String.format("!%s", omit_tag_attr.getValue());// conditional nodes output on true, revert the logic, should not output on true
JptHolderNode holder_node = new JptHolderNode(_isInSlot);
((JptParentNode) pnodes.peek()).appendChild(holder_node);
pnodes.push(holder_node);
JptConditionalNode start_cond = new JptConditionalNode(omit_tag_condition, _isInSlot);
pnodes.push(start_cond);
processStartTag(el);
addStaticFragments();
pnodes.pop();
((JptParentNode) pnodes.peek()).appendChild(start_cond);
pnodes.pop();
// processStartTag(el);
}
else
{
removePNodeChildren();
processStartTag(el);
}
addStaticFragments();
processTalAttributes(el);
JptOutputExpressionNode jout;
if (el.getBaseURI().equals(APPEND_SLOT_NS))
{
jout = new JptOutputExpressionNode(attribute.getValue(), true);
}
else
{
jout = new JptOutputExpressionNode(attribute.getValue(), _isInSlot);
}
((JptParentNode) pnodes.peek()).appendChild(jout);
if (omit_tag_attr != null)
{
String omit_tag_condition = StringUtils.isBlank(omit_tag_attr.getValue()) ? "false" : String.format("!%s", omit_tag_attr.getValue());// conditional nodes output on true, revert the logic, should not output on true
JptConditionalNode end_cond = new JptConditionalNode(omit_tag_condition, _isInSlot);
pnodes.push(end_cond);
processEndTag(el);
addStaticFragments();
pnodes.pop(); // remove conditional node
((JptParentNode) pnodes.peek()).appendChild(end_cond);
}
else
{
processEndTag(el);
}
el.removeChildren();
addStaticFragments();
}
}
private void processMetalUseMacro(Element el) throws ValidityException, ParsingException, IOException
{
Attribute attribute = XomUtils.getAttribute(el, "metal:use-macro");
if (attribute != null)
{
el.removeAttribute(attribute);
String use_macro_value = attribute.getValue();
URI macroURI = _templateUri.resolve(use_macro_value);
// String macroName = extractMacroName(macroURI.getQuery());
String macroPath = macroURI.getPath();
URI macroUri = _templateUri.resolve(macroPath);
Dependency d = new Dependency(macroUri);
_dependecies.add(d);
Map<String, String> macroParams = extractMacroParams(macroURI.getRawQuery());
Document document_macro = XomDocumentBuilder.getDocument(macroUri);
prepareMacro(macroURI, document_macro, el, macroPath, macroParams);
}
}
private void childAppender(Element root, Node child)
{
if (child == null)
{
return;
}
root.setBaseURI(APPEND_SLOT_NS);
int appendChildCount = child.getChildCount();
for (int i = 0; i < appendChildCount; i++)
{
Node childNode = child.copy().getChild(i);
if (childNode != null)
{
childNode.detach();
root.appendChild(childNode);
if (childNode instanceof Element)
{
Element rchild = (Element) child;
rchild.setBaseURI(APPEND_SLOT_NS);
int arChildCount = rchild.getChildCount();
for (int j = 0; j < arChildCount; j++)
{
childAppender(rchild, rchild.getChild(j));
}
}
}
}
}
private void processMetalDefineSlot(Element el) throws ValidityException, ParsingException, IOException
{
Attribute attribute = XomUtils.getAttribute(el, "metal:define-slot");
if (attribute != null)
{
el.removeAttribute(attribute);
String current_attribute_value = attribute.getValue();
Element slot_appender_element = (Element) slotAppenders.get(current_attribute_value);
// - append-to-slot
if (slot_appender_element != null)
{
slot_appender_element.detach();
childAppender(el, slot_appender_element);
}
Element slot_filler_element = (Element) slotFillers.get(current_attribute_value);
// - fill-slot
if (slot_filler_element != null)
{
_isInSlot = true;
prepareElement(slot_filler_element);
_isInSlot = false;
return;
}
prepareElement(el);
}
}
private void processTalRepeat(Element el)
{
Attribute attribute = XomUtils.getAttribute(el, "tal:repeat");
if (attribute != null)
{
// System.out.println("JptNodeBuilder.processTalRepeat.el: »»»" + el.toXML() + "«««");
el.removeAttribute(attribute);
String current_attribute_value = attribute.getValue();
addStaticFragments();
String padding = "";
RepeatElements relements = new RepeatElements(current_attribute_value, padding);
JptLoopNode jloop = new JptLoopNode(relements, _isInSlot);
pnodes.push(jloop);
process(el);
addStaticFragments();
processTalReplace(el);
processTalContent(el);
pnodes.pop();
((JptParentNode) pnodes.peek()).appendChild(jloop);
}
}
private void processTalReplace(Element el)
{
Attribute attribute = XomUtils.getAttribute(el, "tal:replace");
if (attribute != null)
{
el.removeAttribute(attribute);
addStaticFragments();
el.removeChildren();
JptOutputExpressionNode jout;
if (el.getBaseURI().equals(APPEND_SLOT_NS))
{
jout = new JptOutputExpressionNode(attribute.getValue(), true);
}
else
{
jout = new JptOutputExpressionNode(attribute.getValue(), _isInSlot);
}
// JptOutputExpressionNode jout = new
// JptOutputExpressionNode(attribute.getValue(), _isInSlot);
removePNodeChildren();
((JptParentNode) pnodes.peek()).appendChild(jout);
}
}
private void removePNodeChildren()
{
}
public ArrayList<Dependency> getDependecies()
{
return _dependecies;
}
}