package jp.aonir.fuzzyxml.internal;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import jp.aonir.fuzzyxml.FuzzyXMLNode;
import jp.aonir.fuzzyxml.FuzzyXMLText;
public class WOHTMLRenderDelegate implements RenderDelegate {
private FuzzyXMLFormatComposite lastNode = null;
private final boolean useStickyWOTags;
// Should this be a user option??
private static final Set<String> STICKY_TAGS;
// TODO: This should be a user option
private static final int LINE_WRAP_LENGTH = 120;
private static final String[] SPACE_EFFECTED_TAGS = {
"a", "img", "u"
};
static {
STICKY_TAGS = new HashSet<String>();
STICKY_TAGS.addAll(Arrays.asList(SPACE_EFFECTED_TAGS));
}
public WOHTMLRenderDelegate() {
this(true);
}
public WOHTMLRenderDelegate(boolean _useStickyWOTags) {
useStickyWOTags = _useStickyWOTags;
}
public void afterCloseTag(FuzzyXMLNode node, RenderContext renderContext,
StringBuffer xmlBuffer) {
FuzzyXMLFormatComposite _node = new FuzzyXMLFormatComposite(node);
if (_node.isComment() && hasNewLineEnd(xmlBuffer)) {
renderContext.appendIndent(xmlBuffer);
}
if (_node.getParentNode() == null) {
xmlBuffer.append("\n");
}
if (_node.isDocumentRoot() || _node.getParentNode().isDocumentRoot()) {
lastNode = null;
}
else {
lastNode = _node;
}
}
public void afterOpenTag(FuzzyXMLNode node, RenderContext renderContext,
StringBuffer xmlBuffer) {
FuzzyXMLFormatComposite _node = new FuzzyXMLFormatComposite(node);
if (renderContext.shouldFormat()) {
renderContext.indent();
}
lastNode = _node;
}
public void afterRender(RenderContext renderContext, StringBuffer xmlBuffer) {
}
public void beforeCloseTag(FuzzyXMLNode node, RenderContext renderContext,
StringBuffer xmlBuffer) {
FuzzyXMLFormatComposite _node = new FuzzyXMLFormatComposite(node);
// System.out.println("beforeCloseTag: " + node + " " + isSticky(_node));
if (renderContext.shouldFormat()) {
renderContext.outdent();
if (renderContext.isShowNewlines() ) {
boolean append_newline = false;
boolean append_space = false;
boolean bufferHasBreakingEnd = (hasWhiteSpaceEnd(xmlBuffer) || hasTagEnd(xmlBuffer));
if (isPreBlock(_node)) {
// Do nothing
} else
if (isSticky(_node)) {
// Do nothing
} else
if (_node.isBreaking() && bufferHasBreakingEnd) {
if (_node.getParentNode().isBreaking())
append_newline = true;
}
if (lastNodeWasHiddenText()) {
append_space = true;
}
if (append_newline) {
if (!hasNewLineEnd(xmlBuffer))
xmlBuffer.append("\n");
renderContext.appendIndent(xmlBuffer);
} else
if (append_space) {
xmlBuffer.append(" ");
}
}
}
}
public boolean beforeOpenTag(FuzzyXMLNode node, RenderContext renderContext, StringBuffer xmlBuffer) {
FuzzyXMLFormatComposite _node = new FuzzyXMLFormatComposite(node);
// System.out.println("beforeOpenTag: " + node + " " + node.getParentNode());
if (renderContext.isShowNewlines() && renderContext.shouldFormat()) {
boolean append_newline = false;
boolean append_space = false;
if (isSticky(_node.getParentNode()) && !lastNodeWasHiddenText()) {
// Do nothing
} else
if (hasTagEnd(xmlBuffer)) {
if (_node.getParentNode().isBreaking())
append_newline = true;
if (lastNodeWasHiddenText()) {
if (_node.isBreaking())
append_newline = true;
else
append_space = true;
}
if (_node.getParentNode().isDocumentRoot())
append_newline = true;
}
if (isText(lastNode) && !lastNodeWasHiddenText()) {
if (_node.getParentNode().isBreaking() && hasWhiteSpaceEnd(xmlBuffer))
append_newline = true;
}
if (append_newline) {
if (!hasNewLineEnd(xmlBuffer))
xmlBuffer.append("\n");
renderContext.appendIndent(xmlBuffer);
} else
if (append_space) {
xmlBuffer.append(" ");
}
}
return true;
}
public void beforeRender(RenderContext renderContext, StringBuffer xmlBuffer) {
}
public boolean renderNode(FuzzyXMLNode node, RenderContext renderContext,
StringBuffer xmlBuffer) {
FuzzyXMLFormatComposite _node = new FuzzyXMLFormatComposite(node);
boolean append_newline = false;
boolean append_space = false;
boolean bufferHasBreakingEnd = (hasWhiteSpaceEnd(xmlBuffer) || hasTagEnd(xmlBuffer));
// System.out.println("renderNode: " + node + node.getParentNode());
if (_node.isHidden()) {
lastNode = _node;
return false;
}
if (renderContext.shouldFormat() && renderContext.isShowNewlines()) {
if (isSticky(lastNode) && _node.isElement()) {
// Do Nothing
} else
if (_node.isText()) {
if (!_node.hasBreakingStart() && !lastNodeWasHiddenText()) {
// Do Nothing
} else
if (_node.parentNode().isBreaking() || lineIsTooLong(xmlBuffer)) {
append_newline = true;
}
} else
if (bufferHasBreakingEnd && _node.parentNode().isBreaking()) {
if (lastNode.isHidden()) {
append_newline = true;
}
}
if (append_newline) {
if (!hasNewLineEnd(xmlBuffer))
xmlBuffer.append("\n");
renderContext.appendIndent(xmlBuffer);
} else
if (append_space) {
xmlBuffer.append(" ");
}
if (_node.isText() && _node.hasLineBreaks()) {
renderTextBlock((FuzzyXMLText)node, renderContext, xmlBuffer);
lastNode = _node;
return false;
}
}
if (!_node.isElement())
lastNode = _node;
return true;
}
private void renderTextBlock(FuzzyXMLText node, RenderContext renderContext, StringBuffer xmlBuffer) {
FuzzyXMLFormatComposite _node = new FuzzyXMLFormatComposite(node);
StringBuffer indent = new StringBuffer();
renderContext.appendIndent(indent);
xmlBuffer.append(node.getValue().trim().replaceAll("\n\\s*", "\n" + indent.toString()));
if (_node.hasBreakingEnd()) {
if (_node.hasLineBreaks()) {
xmlBuffer.append("\n");
} else {
xmlBuffer.append(" ");
}
}
}
private static boolean hasTagEnd(StringBuffer xmlBuffer) {
if (xmlBuffer.length() == 0)
return false;
return xmlBuffer.charAt(xmlBuffer.length() -1) == '>';
}
private static boolean hasNewLineEnd(StringBuffer xmlBuffer) {
if (xmlBuffer.length() == 0)
return false;
return xmlBuffer.charAt(xmlBuffer.length() -1) == '\n';
}
private static boolean hasWhiteSpaceEnd(StringBuffer xmlBuffer) {
if (xmlBuffer.length() == 0)
return false;
return Character.isWhitespace(xmlBuffer.charAt(xmlBuffer.length() - 1));
}
private boolean lastNodeWasHiddenText() {
return lastNode == null || (lastNode.isText() && lastNode.isHidden());
}
private boolean _isStickyTag(FuzzyXMLFormatComposite node) {
return STICKY_TAGS.contains(node.getName().toLowerCase()) || (useStickyWOTags && node.isWOTag());
}
private boolean isSticky(FuzzyXMLFormatComposite node) {
return !lastNodeWasHiddenText() && _isStickyTag(node);
}
private static boolean isPreBlock(FuzzyXMLFormatComposite node) {
return "pre".equalsIgnoreCase(node.getName());
}
private static boolean lineIsTooLong(StringBuffer xmlBuffer) {
int lastLineStart = xmlBuffer.lastIndexOf("\n");
String line;
if (lastLineStart == -1)
line = xmlBuffer.toString();
else
line = xmlBuffer.substring(lastLineStart);
return (line.length() > LINE_WRAP_LENGTH);
}
private static boolean isText(FuzzyXMLFormatComposite node) {
if (node == null)
return false;
return node.isText();
}
}