diff --git a/schemacrawler-utility/src/main/java/us/fatehi/utility/html/Tag.java b/schemacrawler-utility/src/main/java/us/fatehi/utility/html/Tag.java index 8ab767be90..486d1aaeee 100644 --- a/schemacrawler-utility/src/main/java/us/fatehi/utility/html/Tag.java +++ b/schemacrawler-utility/src/main/java/us/fatehi/utility/html/Tag.java @@ -28,16 +28,14 @@ package us.fatehi.utility.html; -import static java.util.Objects.requireNonNull; -import static us.fatehi.utility.Utility.isBlank; import static us.fatehi.utility.html.TagOutputFormat.html; import static us.fatehi.utility.html.TagOutputFormat.tsv; - import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Map.Entry; - +import static java.util.Objects.requireNonNull; +import static us.fatehi.utility.Utility.isBlank; import us.fatehi.utility.Color; public class Tag { @@ -79,11 +77,10 @@ protected Tag( } public String addAttribute(final String key, final String value) { - if (!isBlank(key) && !isBlank(value)) { + if (!isBlank(key)) { return attributes.put(key, value); - } else { - return value; } + return value; } public Tag addInnerTag(final Tag tag) { @@ -133,6 +130,55 @@ public String toString() { return getTagName(); } + private void appendAttributes(final StringBuilder buffer) { + for (final Entry attribute : attributes.entrySet()) { + final String value = attribute.getValue(); + buffer.append(" ").append(attribute.getKey()); + if (value != null) { + buffer.append("='").append(value).append("'"); + } + } + } + + private void appendBgColor(final StringBuilder buffer) { + if (bgColor != null && !bgColor.equals(Color.white)) { + buffer.append(" bgcolor='").append(bgColor).append("'"); + } + } + + private void appendClosingTag(final StringBuilder buffer) { + if (emphasizeText) { + buffer.append(""); + } + if (indent) { + buffer.append("\t"); + } + buffer.append(""); + } + + private void appendEmphasizedText(final StringBuilder buffer) { + if (emphasizeText) { + buffer.append(""); + } + } + + private void appendInnerTags(final StringBuilder buffer) { + for (final Tag innerTag : innerTags) { + if (indent) { + buffer.append("\t"); + } + buffer.append("\t").append(innerTag.render(html)).append(System.lineSeparator()); + } + } + + private void appendStyleClass(final StringBuilder buffer) { + if (!isBlank(styleClass)) { + buffer.append(" class='").append(styleClass).append("'"); + } else if (align != null && align != Alignment.inherit) { + buffer.append(" align='").append(align).append("'"); + } + } + /** * Escapes the characters in text for use in HTML. * @@ -172,49 +218,22 @@ private String toHtmlString() { buffer.append("\t"); } buffer.append("<").append(getTagName()); - for (final Entry attribute : attributes.entrySet()) { - buffer - .append(" ") - .append(attribute.getKey()) - .append("='") - .append(attribute.getValue()) - .append("'"); - } - if (bgColor != null && !bgColor.equals(Color.white)) { - buffer.append(" bgcolor='").append(bgColor).append("'"); - } - if (!isBlank(styleClass)) { - buffer.append(" class='").append(styleClass).append("'"); - } else if (align != null && align != Alignment.inherit) { - buffer.append(" align='").append(align).append("'"); - } + appendAttributes(buffer); + appendBgColor(buffer); + appendStyleClass(buffer); buffer.append(">"); - if (emphasizeText) { - buffer.append(""); - } + appendEmphasizedText(buffer); - if (innerTags.isEmpty()) { - if (indent) { - buffer.append(System.lineSeparator()); - } - buffer.append(escapeText ? escapeHtml(text) : text); - } else { + if (indent) { buffer.append(System.lineSeparator()); - for (final Tag innerTag : innerTags) { - if (indent) { - buffer.append("\t"); - } - buffer.append("\t").append(innerTag.render(html)).append(System.lineSeparator()); - } } + buffer.append(escapeText ? escapeHtml(text) : text); - if (emphasizeText) { - buffer.append(""); + if (!innerTags.isEmpty()) { + appendInnerTags(buffer); } - if (indent) { - buffer.append("\t"); - } - buffer.append(""); + + appendClosingTag(buffer); return buffer.toString(); } @@ -253,17 +272,15 @@ private String toInnerTagsTsvString() { * @return Text */ private String toPlainTextString() { - if (innerTags.isEmpty()) { - if (characterWidth > 0) { - final String format = - String.format("%%%s%ds", align == Alignment.right ? "" : "-", characterWidth); - return String.format(format, text); - } else { - return text; - } - } else { + if (!innerTags.isEmpty()) { return toInnerTagsPlainTextString(); } + if (characterWidth > 0) { + final String format = + String.format("%%%s%ds", align == Alignment.right ? "" : "-", characterWidth); + return String.format(format, text); + } + return text; } /** @@ -274,8 +291,7 @@ private String toPlainTextString() { private String toTsvString() { if (innerTags.isEmpty()) { return text; - } else { - return toInnerTagsTsvString(); } + return toInnerTagsTsvString(); } } diff --git a/schemacrawler-utility/src/test/java/us/fatehi/utility/test/html/CaptionTest.java b/schemacrawler-utility/src/test/java/us/fatehi/utility/test/html/CaptionTest.java index 29a5a87819..2899d19c4e 100644 --- a/schemacrawler-utility/src/test/java/us/fatehi/utility/test/html/CaptionTest.java +++ b/schemacrawler-utility/src/test/java/us/fatehi/utility/test/html/CaptionTest.java @@ -34,13 +34,11 @@ import static org.hamcrest.Matchers.nullValue; import static us.fatehi.utility.html.TagBuilder.caption; import static us.fatehi.utility.html.TagBuilder.span; - import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; - import us.fatehi.utility.Color; import us.fatehi.utility.html.Tag; import us.fatehi.utility.html.TagOutputFormat; @@ -73,7 +71,7 @@ public void caption1() { assertThat(captionElement.attr("sometag"), is("customvalue")); assertThat(captionElement.attr("bgcolor"), is("#FF0064")); assertThat(captionElement.attr("class"), is("class")); - assertThat(captionElement.text(), is("display text")); + assertThat(captionElement.text(), is("display text display text")); assertThat(captionElement.select("span").text(), is("display text")); assertThat(caption.render(TagOutputFormat.text), is("display text")); diff --git a/schemacrawler-utility/src/test/java/us/fatehi/utility/test/html/TableCellTest.java b/schemacrawler-utility/src/test/java/us/fatehi/utility/test/html/TableCellTest.java index f418e1a491..1ae61f9e9c 100644 --- a/schemacrawler-utility/src/test/java/us/fatehi/utility/test/html/TableCellTest.java +++ b/schemacrawler-utility/src/test/java/us/fatehi/utility/test/html/TableCellTest.java @@ -35,13 +35,11 @@ import static org.hamcrest.Matchers.nullValue; import static us.fatehi.utility.html.TagBuilder.tableCell; import static us.fatehi.utility.html.TagBuilder.tableHeaderCell; - import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; - import us.fatehi.utility.Color; import us.fatehi.utility.html.Alignment; import us.fatehi.utility.html.Tag; @@ -74,8 +72,6 @@ public void td1() { Jsoup.parseBodyFragment(String.format("%s
", renderedHtml)); final Element td = doc.select("td").first(); - System.out.println(renderedHtml); - System.out.println(td.attributes()); assertThat(td.attr("sometag"), is("customvalue")); assertThat(td.attr("bgcolor"), is("#FF0064")); assertThat(td.attr("class"), is("class")); @@ -105,8 +101,6 @@ public void td2() { final Element td = doc.select("td").first(); assertThat(renderedHtml, not(nullValue())); - System.out.println(renderedHtml); - System.out.println(td.attributes()); assertThat(td.attr("sometag"), is("custom&value")); assertThat(td.attr("colspan"), is("2")); assertThat(td.attr("align"), is("right")); @@ -142,8 +136,6 @@ public void th1() { Jsoup.parseBodyFragment(String.format("%s
", renderedHtml)); final Element th = doc.select("th").first(); - System.out.println(renderedHtml); - System.out.println(th.attributes()); assertThat(th.attr("sometag"), is("customvalue")); assertThat(th.attr("bgcolor"), is("#FF0064")); assertThat(th.attr("class"), is("class")); diff --git a/schemacrawler-utility/src/test/java/us/fatehi/utility/test/html/TableRowTest.java b/schemacrawler-utility/src/test/java/us/fatehi/utility/test/html/TableRowTest.java index e96c9c4c98..4b0887391a 100644 --- a/schemacrawler-utility/src/test/java/us/fatehi/utility/test/html/TableRowTest.java +++ b/schemacrawler-utility/src/test/java/us/fatehi/utility/test/html/TableRowTest.java @@ -34,9 +34,7 @@ import static org.hamcrest.Matchers.nullValue; import static us.fatehi.utility.html.TagBuilder.tableCell; import static us.fatehi.utility.html.TagBuilder.tableRow; - import org.junit.jupiter.api.Test; - import us.fatehi.utility.html.Tag; import us.fatehi.utility.html.TagOutputFormat; diff --git a/schemacrawler-utility/src/test/java/us/fatehi/utility/test/html/TagTest.java b/schemacrawler-utility/src/test/java/us/fatehi/utility/test/html/TagTest.java new file mode 100644 index 0000000000..06e32ea0d3 --- /dev/null +++ b/schemacrawler-utility/src/test/java/us/fatehi/utility/test/html/TagTest.java @@ -0,0 +1,200 @@ +/* +======================================================================== +SchemaCrawler +http://www.schemacrawler.com +Copyright (c) 2000-2024, Sualeh Fatehi . +All rights reserved. +------------------------------------------------------------------------ + +SchemaCrawler is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +SchemaCrawler and the accompanying materials are made available under +the terms of the Eclipse Public License v1.0, GNU General Public License +v3 or GNU Lesser General Public License v3. + +You may elect to redistribute this code under any of these licenses. + +The Eclipse Public License is available at: +http://www.eclipse.org/legal/epl-v10.html + +The GNU General Public License v3 and the GNU Lesser General Public +License v3 are available at: +http://www.gnu.org/licenses/ + +======================================================================== +*/ + +package us.fatehi.utility.test.html; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; +import static us.fatehi.utility.html.TagOutputFormat.html; +import static us.fatehi.utility.html.TagOutputFormat.text; +import static us.fatehi.utility.html.TagOutputFormat.tsv; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import us.fatehi.utility.Color; +import us.fatehi.utility.html.Alignment; +import us.fatehi.utility.html.Tag; +import us.fatehi.utility.html.TagBuilder; + +public class TagTest { + + @DisplayName("toHtmlString: basic output") + @Test + public void toHtmlString_basic() { + final Tag tag = + TagBuilder.span() + .withText("display text") + .withWidth(2) + .withAlignment(Alignment.right) + .withStyleClass("class") + .withBackground(Color.fromRGB(255, 0, 100)) + .make(); + tag.addAttribute("sometag", "customvalue"); + tag.addAttribute(null, "nullvalue"); + tag.addAttribute("nulltag", null); + tag.addAttribute("emptytag", ""); + + assertThat(tag.render(text), is("display text")); + assertThat(tag.render(tsv), is("display text")); + + final Element span = parseRenderedHtml(tag); + + assertThat(span.attributesSize(), is(5)); + assertThat(span.attr("sometag"), is("customvalue")); + assertThat(span.attr("nulltag"), is("")); + assertThat(span.attr("emptytag"), is("")); + assertThat(span.attr("bgcolor"), is("#FF0064")); + assertThat(span.attr("class"), is("class")); + assertThat(span.text(), is("display text")); + } + + @DisplayName("toHtmlString: escape text, emphasize, and allow free width left aligned") + @Test + public void toHtmlString_escapeEmphasize() { + final Tag tag = + TagBuilder.span() + .withEscapedText("display & text") + .withAlignment(Alignment.left) + .withEmphasis() + .make(); + tag.addAttribute("sometag", "custom&value"); + + assertThat(tag.render(text), is("display & text")); + assertThat(tag.render(tsv), is("display & text")); + + final Element span = parseRenderedHtml(tag); + + assertThat(span.attr("sometag"), is("custom&value")); + assertThat(span.attr("align"), is("left")); + assertThat(span.text(), is("display & text")); + assertThat(span.select("b").first().outerHtml(), is("display & text")); + } + + @DisplayName("toHtmlString: inner tags") + @Test + public void toHtmlString_innerTags() { + final Tag outerTag = + TagBuilder.span() + .withText("outer text") + .withWidth(2) + .withAlignment(Alignment.right) + .withStyleClass("class") + .withBackground(Color.fromRGB(255, 0, 100)) + .make(); + outerTag.addAttribute("sometag", "customvalue"); + + final Tag innerTag = TagBuilder.span().withText("inner text").make(); + outerTag.addInnerTag(innerTag); + + // Test adding a null inner tag + outerTag.addInnerTag(null); + + assertThat(outerTag.render(text), is("inner text")); + assertThat(outerTag.render(tsv), is("inner text")); + + final Element outerSpan = parseRenderedHtml(outerTag); + final Element innerSpan = outerSpan.select("span").get(1); + + assertThat(outerSpan.attr("sometag"), is("customvalue")); + assertThat(outerSpan.attr("bgcolor"), is("#FF0064")); + assertThat(outerSpan.attr("class"), is("class")); + assertThat(outerSpan.text(), is("outer text inner text")); + + assertThat(innerSpan.text(), is("inner text")); + } + + @DisplayName("toHtmlString: bgcolor") + @Test + public void toHtmlString_bgcolor() { + + // Test with color + + final Tag tagBgColor = + TagBuilder.span() + .withText("text - color") + .withBackground(Color.fromRGB(255, 0, 100)) + .make(); + + assertThat(tagBgColor.render(text), is("text - color")); + assertThat(tagBgColor.render(tsv), is("text - color")); + + final Element spanBgColor = parseRenderedHtml(tagBgColor); + + assertThat(spanBgColor.attributesSize(), is(1)); + assertThat(spanBgColor.attr("bgcolor"), is("#FF0064")); + assertThat(spanBgColor.text(), is("text - color")); + + // Test with no color + + final Tag tagNoBg = TagBuilder.span().withText("text - no color").make(); + + assertThat(tagNoBg.render(text), is("text - no color")); + assertThat(tagNoBg.render(tsv), is("text - no color")); + + final Element spanNoBg = parseRenderedHtml(tagNoBg); + + assertThat(spanNoBg.attributesSize(), is(0)); + assertThat(spanNoBg.attr("bgcolor"), is("")); + assertThat(spanNoBg.text(), is("text - no color")); + + // Test with white + + final Tag tagBgWhite = + TagBuilder.span().withText("text - white").withBackground(Color.white).make(); + + assertThat(tagBgWhite.render(text), is("text - white")); + assertThat(tagBgWhite.render(tsv), is("text - white")); + + final Element spanBgWhite = parseRenderedHtml(tagBgWhite); + + assertThat(spanBgWhite.attributesSize(), is(0)); + assertThat(spanBgWhite.attr("bgcolor"), is("")); + assertThat(spanBgWhite.text(), is("text - white")); + } + + /** + * Use jsoup to ensure that the rendered HTML can be parsed, and check attributes without relying + * on the order that they are generated + * + * @param tag Tag to render + * @return Top level HTML element + */ + private Element parseRenderedHtml(final Tag tag) { + + final String renderedHtml = tag.render(html); + assertThat(renderedHtml, is(not(nullValue()))); + + final Document doc = Jsoup.parseBodyFragment(renderedHtml); + final Element span = doc.select(tag.getTagName()).first(); + return span; + } +} diff --git a/schemacrawler-utility/src/test/java/us/fatehi/utility/test/string/ObjectToStringTest.java b/schemacrawler-utility/src/test/java/us/fatehi/utility/test/string/ObjectToStringTest.java index 2a146fd51a..688a6a4716 100644 --- a/schemacrawler-utility/src/test/java/us/fatehi/utility/test/string/ObjectToStringTest.java +++ b/schemacrawler-utility/src/test/java/us/fatehi/utility/test/string/ObjectToStringTest.java @@ -3,11 +3,8 @@ import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; - import java.nio.file.AccessMode; - import org.junit.jupiter.api.Test; - import us.fatehi.test.utility.TestObject; import us.fatehi.test.utility.TestObjectUtility; import us.fatehi.utility.ObjectToString; @@ -42,14 +39,6 @@ public void listOrObjectToString() { containsString(TestObject.class.getName())); } - @Test - public void serialize() { - - final TestObject testObject = TestObjectUtility.makeTestObject(); - - System.out.println(ObjectToString.toString(testObject)); - } - @Test public void toStringTest() { assertThat(ObjectToString.toString(null), is("null"));