2
\$\begingroup\$

(See the next iteration.)

I have this tiny library for building HTML pages in Java code:

HtmlViewComponent.java

package net.coderodde.html.view;
/**
 * This abstract class defines the API for logical HTML view components, that 
 * may consist of other view components;
 * 
 * @author Rodion "rodde" Efremov
 * @version 1.6 (Mar 18, 2016)
 */
public abstract class HtmlViewComponent {
 @Override
 public abstract String toString();
}

HtmlViewContainer.java

package net.coderodde.html.view;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
 * This class defines the API for HTML elements that may contain other HTML
 * elements.
 * 
 * @author Rodion "rodde" Efremov
 * @version 1.6 (Mar 18, 2016)
 */
public abstract class HtmlViewContainer extends HtmlViewComponent {
 protected final List<HtmlViewComponent> components = new ArrayList<>();
 public void addHtmlViewComponent(HtmlViewComponent component) {
 Objects.requireNonNull(component, "The input component is null.");
 components.add(component);
 }
 public boolean containsHtmlViewComponent(HtmlViewComponent component) {
 Objects.requireNonNull(component, "The input component is null.");
 return components.contains(component);
 }
 public void removeHtmlViewComponent(HtmlViewComponent component) {
 Objects.requireNonNull(component, "The input component is null.");
 components.remove(component);
 }
 @Override
 public abstract String toString();
}

HtmlPage.java

package net.coderodde.html.view;
/**
 * This class is the top-level container of view components.
 * 
 * @author Rodion "rodde" Efremov
 * @version 1.6 (Mar 18, 2016)
 */
public class HtmlPage extends HtmlViewContainer {
 private final String title;
 public HtmlPage(String title) {
 this.title = title != null ? title : "";
 }
 @Override
 public String toString() {
 StringBuilder sb = new StringBuilder().append("<!DOCTYPE html>\n")
 .append("<html>\n")
 .append("<head>\n")
 .append("<title>")
 .append(title)
 .append("</title>\n")
 .append("</head>\n")
 .append("<body>\n");
 components.stream().forEach((component) -> {
 sb.append(component.toString());
 });
 return sb.append("</body>\n")
 .append("</html>").toString();
 }
}

DivComponent.java

package net.coderodde.html.view.support;
import net.coderodde.html.view.HtmlViewContainer;
/**
 * This class implements a {@code div} component.
 * 
 * @author Rodion "rodde" Efremov
 * @version 1.6 (Mar 18, 2016)
 */
public class DivComponent extends HtmlViewContainer {
 @Override
 public String toString() {
 StringBuilder sb = new StringBuilder("<div>\n");
 components.stream().forEach((component) -> {
 sb.append(component.toString());
 });
 return sb.append("</div>\n").toString();
 }
}

TableComponent.java

package net.coderodde.html.view.support;
import java.util.ArrayList;
import java.util.List;
import net.coderodde.html.view.HtmlViewComponent;
/**
 * This class represents the table component.
 * 
 * @author Rodion "rodde" Efremov
 * @version 1.6 (Mar 18, 2016)
 */
public class TableComponent extends HtmlViewComponent {
 private final int columns;
 public TableComponent(int columns) {
 checkColumnNumber(columns);
 this.columns = columns;
 }
 private final List<List<? extends HtmlViewComponent>> table = new ArrayList<>();
 public void addRow(List<HtmlViewComponent> row) {
 while (row.size() > columns) {
 row.remove(row.size() - 1);
 }
 table.add(row);
 }
 private void checkColumnNumber(int columns) {
 if (columns <= 0) {
 throw new IllegalArgumentException(
 "The number of columns must be a positive integer. " +
 "Received " + columns);
 }
 }
 @Override
 public String toString() {
 StringBuilder sb = new StringBuilder().append("<table>\n");
 for (List<? extends HtmlViewComponent> row : table) {
 sb.append("<tr>");
 for (HtmlViewComponent cell : row) {
 sb.append("<td>");
 sb.append(cell.toString());
 sb.append("</td>");
 }
 for (int i = row.size(); i < columns; ++i) {
 sb.append("<td></td>");
 }
 sb.append("</tr>\n");
 }
 return sb.append("</table>\n").toString();
 }
}

TextComponent.java

package net.coderodde.html.view.support;
import net.coderodde.html.view.HtmlViewComponent;
/**
 * This class represents a simple text.
 * 
 * @author Rodion "rodde" Efremov
 * @version 1.6 (Mar 18, 2016)
 */
public class TextComponent extends HtmlViewComponent {
 private String text; 
 public TextComponent() {
 this("");
 }
 public TextComponent(String text) {
 setText(text);
 }
 public void setText(String text) {
 this.text = text != null ? text : "";
 }
 public String getText() {
 return text;
 }
 @Override
 public String toString() {
 return text;
 }
}

Demo.java

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import net.coderodde.html.view.HtmlPage;
import net.coderodde.html.view.HtmlViewComponent;
import net.coderodde.html.view.support.DivComponent;
import net.coderodde.html.view.support.TableComponent;
import net.coderodde.html.view.support.TextComponent;
public class Demo {
 public static void main(String[] args) {
 HtmlPage page = new HtmlPage("FUNKEEH PAGE");
 DivComponent div1 = new DivComponent();
 DivComponent div2 = new DivComponent();
 div1.addHtmlViewComponent(new TextComponent("Hey yo!\n"));
 TableComponent table = new TableComponent(3);
 // Arrays.asList is immutable, so copy to a mutable array list.
 List<HtmlViewComponent> row1 = new ArrayList<>(
 Arrays.asList(new TextComponent("Row 1, column 1"),
 new TextComponent("Row 1, column 2"),
 new TextComponent("Row 1, column 3"),
 new TextComponent("FAIL")));
 List<HtmlViewComponent> row2 = new ArrayList<>(
 Arrays.asList(new TextComponent("Row 2, column 1"),
 new TextComponent("Row 2, column 2")));
 table.addRow(row1);
 table.addRow(row2);
 div2.addHtmlViewComponent(table);
 div2.addHtmlViewComponent(new TextComponent("Bye, bye!\n"));
 page.addHtmlViewComponent(div1);
 page.addHtmlViewComponent(div2);
 System.out.println(page);
 }
}

I don't think it is of any use, but I want to hear comments on the hierarchy aspect. Please, tell me anything that comes to mind.

asked Mar 18, 2016 at 16:09
\$\endgroup\$

1 Answer 1

5
\$\begingroup\$

Since HtmlViewComponent has no implementation only a method declaration, it would be more natural to use an interface instead.

In addRow, when there are more columns than allowed in the table, instead of mutating the input list by deleting elements, it would be better to call subList. It will not modify the input list and more efficient too.

You don't need to call .toString() in code like sb.append(obj.toString()), it gets called automatically.

toString is not really designed for display purposes. It's also strange to see abstract String toString declarations. Since your methods are about HTML formatting, toHtml would be a better name.

answered Mar 18, 2016 at 17:07
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.