🎉 Exercism Research is now launched. Help Exercism, help science and have some fun at research.exercism.io 🎉
Avatar of dandek

dandek's solution

to Markdown in the Java Track

Published at Jul 13 2018 · 1 comment
Instructions
Test suite
Solution

Note:

This solution was written on an old version of Exercism. The tests below might not correspond to the solution code, and the exercise may have changed since this code was written.

Refactor a Markdown parser.

The markdown exercise is a refactoring exercise. There is code that parses a given string with Markdown syntax and returns the associated HTML for that string. Even though this code is confusingly written and hard to follow, somehow it works and all the tests are passing! Your challenge is to re-write this code to make it easier to read and maintain while still making sure that all the tests keep passing.

It would be helpful if you made notes of what you did in your refactoring in comments so reviewers can see that, but it isn't strictly necessary. The most important thing is to make the code better!

Running the tests

You can run all the tests for an exercise by entering

$ gradle test

in your terminal.

Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.

MarkdownTest.java

import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;

import static org.junit.Assert.assertEquals;

public class MarkdownTest {

    private Markdown markdown;

    @Before
    public void setup() {
        markdown = new Markdown();
    }

    @Test
    public void normalTextAsAParagraph() {
        String input = "This will be a paragraph";
        String expected = "<p>This will be a paragraph</p>";

        assertEquals(expected, markdown.parse(input));
    }

    @Ignore("Remove to run test")
    @Test
    public void italics() {
        String input = "_This will be italic_";
        String expected = "<p><em>This will be italic</em></p>";

        assertEquals(expected, markdown.parse(input));
    }

    @Ignore("Remove to run test")
    @Test
    public void boldText() {
        String input = "__This will be bold__";
        String expected = "<p><strong>This will be bold</strong></p>";

        assertEquals(expected, markdown.parse(input));
    }

    @Ignore("Remove to run test")
    @Test
    public void normalItalicsAndBoldText() {
        String input = "This will _be_ __mixed__";
        String expected = "<p>This will <em>be</em> <strong>mixed</strong></p>";

        assertEquals(expected, markdown.parse(input));
    }

    @Ignore("Remove to run test")
    @Test
    public void withH1HeaderLevel() {
        String input = "# This will be an h1";
        String expected = "<h1>This will be an h1</h1>";

        assertEquals(expected, markdown.parse(input));
    }

    @Ignore("Remove to run test")
    @Test
    public void withH2HeaderLevel() {
        String input = "## This will be an h2";
        String expected = "<h2>This will be an h2</h2>";

        assertEquals(expected, markdown.parse(input));
    }

    @Ignore("Remove to run test")
    @Test
    public void withH6HeaderLevel() {
        String input = "###### This will be an h6";
        String expected = "<h6>This will be an h6</h6>";

        assertEquals(expected, markdown.parse(input));
    }

    @Ignore("Remove to run test")
    @Test
    public void unorderedLists() {
        String input = "* Item 1\n* Item 2";
        String expected = "<ul><li>Item 1</li><li>Item 2</li></ul>";

        assertEquals(expected, markdown.parse(input));
    }

    @Ignore("Remove to run test")
    @Test
    public void aLittleBitOfEverything() {
        String input = "# Header!\n* __Bold Item__\n* _Italic Item_";
        String expected = "<h1>Header!</h1><ul><li><strong>Bold Item</strong></li><li><em>Italic Item</em></li></ul>";

        assertEquals(expected, markdown.parse(input));
    }
}
import java.util.Arrays;
import java.util.stream.Collectors;

class Markdown {

    String parse(String input) {
        StringBuilder res = new StringBuilder();
        boolean start_list = true;
        String pre;
        for (String line : input.split("\n")) {
            line = Arrays.stream(line.split(" "))
                    .map(this::emphasis)
                    .collect(Collectors.joining(" "));

            switch (line.charAt(0)) {
                case '#':
                    int nbr = line.indexOf(" ");
                    pre = "h" + String.valueOf(nbr);
                    line = line.substring(nbr + 1);
                    break;
                case '*':
                    if (start_list) {
                        res.append("<ul>");
                        start_list = false;
                    }
                    pre = "li";
                    start_list = false;
                    line = emphasis(line.substring(2));
                    break;
                default:
                    pre = "p";
            }
            res.append("<").append(pre).append(">").append(emphasis(line)).append("</").append(pre).append(">");
        }
        if (!start_list)
            res.append("</ul>");
        return res.toString();
    }

    private String emphasis(String text) {
        if (text.startsWith("__") && text.endsWith("__"))
            return "<strong>" + text.substring(2, text.length() - 2) + "</strong>";
        if (text.startsWith("_") && text.endsWith("_"))
            return "<em>" + text.substring(1, text.length() - 1) + "</em>";
        return text;
    }
}

Community comments

Find this solution interesting? Ask the author a question to learn more.
Avatar of hrodward

Elegant and readable!

What can you learn from this solution?

A huge amount can be learned from reading other people’s code. This is why we wanted to give exercism users the option of making their solutions public.

Here are some questions to help you reflect on this solution and learn the most from it.

  • What compromises have been made?
  • Are there new concepts here that you could read more about to improve your understanding?