// Copyright (C) 2003-2009 by Object Mentor, Inc. All rights reserved. // Released under the terms of the CPL Common Public License version 1.0. package fitnesse.html; import java.util.Iterator; import java.util.LinkedList; import java.util.List; public class HtmlTag extends HtmlElement implements Iterable<HtmlElement> { private final LinkedList<HtmlElement> childTags = new LinkedList<>(); private final List<Attribute> attributes = new LinkedList<>(); private final String tagName; private boolean isInline; public HtmlTag(String tagName) { this.tagName = tagName; } public HtmlTag(String tagName, String content) { this(tagName); add(content); } public HtmlTag(String tagName, HtmlElement child) { this(tagName); add(child); } public String tagName() { return tagName; } @Override public String html() { return html(0); } public String htmlInline() { isInline = true; return html(0); } public String html(int depth) { return new HtmlFormatter(depth).format(); } private boolean hasChildren() { return !childTags.isEmpty(); } public void add(String s) { add(new RawHtml(s)); } public void add(HtmlElement element) { childTags.add(element); } public void addAttribute(String key, String value) { attributes.add(new Attribute(key, value)); } public void use(String s) { use(new RawHtml(s)); } public void use(HtmlElement element) { childTags.clear(); add(element); } public String getAttribute(String key) { for (Attribute attribute : attributes) { if (key != null && key.equals(attribute.name)) return attribute.value; } return null; } protected String makeIndent(int depth) { StringBuilder indent = new StringBuilder(); for (int i = 0; i < depth; i++) indent.append('\t'); return indent.toString(); } @Override public Iterator<HtmlElement> iterator() { return childTags.iterator(); } public static class Attribute { public String name; public String value; public Attribute(String name, String value) { this.name = name; this.value = value; } } private class HtmlFormatter { private int depth; private boolean childTagWasMade; private boolean lastMadeChildWasNotTag; private boolean firstElement; public HtmlFormatter(int depth) { this.depth = depth; } public String format() { return makeTabs() + makeTag() + makeAttributes() + makeTagEnd() + makeChildren() + makeEndTag() + makeLineEnd(); } private String makeEndTag() { return hasChildren() ? "</" + tagName() + ">" : ""; } private String makeLineEnd() { return isInline ? "" : endl; } private String makeChildren() { String children = ""; if (hasChildren()) { children = makeChildrenWithoutTrailingIndent(); if (childTagWasMade && !isInline) children += makeTabs(); } return children; } private String makeChildrenWithoutTrailingIndent() { StringBuilder children = new StringBuilder(64); childTagWasMade = false; lastMadeChildWasNotTag = false; firstElement = true; for (HtmlElement element : childTags) { children.append(makeChildFromElement(element)); firstElement = false; } return children.toString(); } private String makeChildFromElement(HtmlElement element) { boolean childIsTag = element instanceof HtmlTag; String child = childIsTag ? makeChildFromTag((HtmlTag) element) : element.html(); prepareForNextElement(childIsTag); return child; } private void prepareForNextElement(boolean childIsTag) { childTagWasMade |= childIsTag; lastMadeChildWasNotTag = !childIsTag; } private String makeChildFromTag(HtmlTag element) { return (childShouldStartWithNewLine() ? endl : "") + element.html(depth + 1); } private boolean childShouldStartWithNewLine() { return (firstElement || lastMadeChildWasNotTag) && !isInline; } private String makeTagEnd() { return hasChildren() ? ">" : "/>"; } private String makeAttributes() { StringBuilder attributes = new StringBuilder(); for (Attribute attribute : HtmlTag.this.attributes) { attributes.append(" ").append(attribute.name).append("=\"").append(attribute.value).append("\""); } return attributes.toString(); } private String makeTag() { return "<" + tagName(); } private String makeTabs() { return makeIndent(depth); } } }