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.

\$\endgroup\$
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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.