How to Parse HTML/CSS Colors in Java


The challenge

In this challenge, you parse RGB colors represented by strings. The formats are primarily used in HTML and CSS. Your task is to implement a function that takes a color as a string and returns the parsed color as a map (see Examples).

Inputs:

The input string represents one of the following:

  • 6-digit hexadecimal – “#RRGGBB”
    e.g. “#012345”, “#789abc”, “#FFA077”
    Each pair of digits represents a value of the channel in hexadecimal: 00 to FF
  • 3-digit hexadecimal – “#RGB”
    e.g. “#012”, “#aaa”, “#F5A”
    Each digit represents a value 0 to F which translates to 2-digit hexadecimal: 0->00, 1->11, 2->22, and so on.
  • Preset color name
    e.g. “red”, “BLUE”, “LimeGreen”
    You have to use the predefined map PRESET_COLORS (JavaScript, Python, Ruby), presetColors (Java, C#, Haskell), or preset-colors (Clojure). The keys are the names of preset colors in lower-case and the values are the corresponding colors in 6-digit hexadecimal (same as 1. “#RRGGBB”).

Examples:

parse("#80FFA0") === new RGB(128, 255, 160))
parse("#3B7") === new RGB( 51, 187, 119))
parse("LimeGreen") === new RGB( 50, 205,  50))

// RGB class is defined as follows:
final class RGB {
    public int r, g, b;
    
    public RGB();
    public RGB(int r, int g, int b);
}

The solution in Java code

Option 1:

import java.util.Map;
import java.awt.Color;

public class HtmlColorParser {
    private final Map<String, String> presetColors;
    public HtmlColorParser(Map<String, String> presetColors) {
        this.presetColors = presetColors;
    }
    public RGB parse(String color) {
        if (color.charAt(0) != '#') color = nameFixer(color);
        if (color.length() < 7) color = stringFixer(color);
        Color decoded = (Color.decode(color));
        return new RGB(decoded.getRed(), decoded.getGreen(), decoded.getBlue());
    }
    public String stringFixer(String s) {
        return "#" + s.charAt(1) + s.charAt(1) + s.charAt(2) + s.charAt(2) + s.charAt(3) + s.charAt(3);
    }
    public String nameFixer(String s) {
        return presetColors.get(s.toLowerCase());
    }
}

Option 2:

import java.util.Map;

class HtmlColorParser {
  private final Map<String, String> presetColors;
  HtmlColorParser(Map<String, String> presetColors) {
    this.presetColors = presetColors;
  }
  RGB parse(String color) {
    if ((color = presetColors.getOrDefault(color.toLowerCase(), color)).length() < 7)
      color = color.replaceAll("((?i)[\\da-f])", "$1$1");
    return new RGB(Integer.valueOf(color.substring(1, 3), 16), Integer.valueOf(color.substring(3, 5), 16), Integer.valueOf(color.substring(5), 16));
  }
}

Option 3:

import java.util.Map;

public class HtmlColorParser {
    private final Map<String, String> presetColors;
    public HtmlColorParser(Map<String, String> presetColors) {
        this.presetColors = presetColors;
    }
    public RGB parse(String color) {
            String lc = color.toLowerCase();
            String rgb = presetColors.getOrDefault(lc, lc);
            if(rgb.length() == 4)
                rgb = rgb.replaceAll("([0-9a-f])", "$1$1");
            return new RGB(Integer.valueOf(rgb.substring(1, 3), 16), 
                           Integer.valueOf(rgb.substring(3, 5), 16),
                           Integer.valueOf(rgb.substring(5), 16));
    }
}

Test cases to validate our solution

import java.util.Locale;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;

public class ExampleTests {
    private HtmlColorParser parser;
    @Before
    public void setup() {
        parser = new HtmlColorParser(PresetColors.getMap());
    }
    @Test
    public void testExamples() {
        shouldParse("#80FFA0", new RGB(128, 255, 160));
        shouldParse("#3B7", new RGB( 51, 187, 119));
        shouldParse("LimeGreen", new RGB( 50, 205,  50));
    }
    private void shouldParse(String color, RGB expected) {
        assertRgbEquals(color, expected, parser.parse(color));
    }
    private static void assertRgbEquals(String input, RGB expected, RGB actual) throws AssertionError {
        try {
            System.out.printf("input: \"%s\"", input);
            assertEquals(expected, actual);
            System.out.println(" => pass!");
        } catch (AssertionError e) {
            String message = String.format(Locale.ENGLISH,
                "expected: %s\nactual  : %s", expected, actual);
            throw new AssertionError(message, e);
        }
    }
}