class Flags {

    public void printHelp(OutputStream out) {
        PrintWriter w = new PrintWriter(out);

        Map<String, List<OptionHolder>> holdersByClass = new TreeMap<String, List<OptionHolder>>();

        // Iterate over all the options we have gathered and stash them by class.
        for (OptionHolder holder : options.values()) {
            // Fetch list corresponding to source class name
            final String className;
            if (holder.isInstanced()) {
                className = holder.getObjectSource().getClass().getName();
            } else {
                className = holder.getClassSource().getName();
            }
            List<OptionHolder> holderList = holdersByClass.get(className);
            if (null == holderList) {
                // The list did not exist.  Create it.
                holderList = new LinkedList<OptionHolder>();
                holdersByClass.put(className, holderList);
            }

            holderList.add(holder);
        }

        // Output options by class
        for (Map.Entry<String, List<OptionHolder>> ent : holdersByClass.entrySet()) {
            String className = ent.getKey();
            List<OptionHolder> holderList = ent.getValue();

            // Sort the options. In Java, sorting collections is worse
            // than watching Pandas fuck.
            Collections.sort(holderList, new Comparator<OptionHolder>() {
                @Override
                public int compare(OptionHolder a, OptionHolder b) {
                    return a.getFlag().name().toLowerCase().compareTo(b.getFlag().name().toLowerCase());
                }
            });

            StringBuffer buff = new StringBuffer();

            buff.append("\n\n")
            .append(className)
            .append("\n")
            .append("------------------------------------------------------------------------")
            .append("\n");

            for (OptionHolder holder : holderList) {
                // Mark required flags with a "*"
                buff.append(holder.getFlag().required() ? "* " : "  ");

                String s;
                try {
                    s = "  --" + holder.getFlag().name() + " <" + holder.getType() + "> default: "
                            + (holder.isInstanced()
                                ? holder.getField().get(holder.getObjectSource())
                                : holder.getField().get(holder.getClassSource()));
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }

                //TODO: handle enum options
                if (holder.getFlag().options() != NoOption.class) {
                    s = s + " options: "+enumOptions.get(holder.getFlag().options()).toString();
                }

                // Avert your eyes.
                int spaces = 50 - s.length();
                spaces = spaces < 0 ? 0 : spaces;
                buff.append(s)
                .append("  . . . . . . . . . . . . . . . . . . . . . . . . ".substring(0, spaces))
                .append("| " + holder.getFlag().description())
                .append("\n");
            }
            w.println(buff.toString());
        }
        w.flush();
    }

}

class FlagsTest {

    @Rule
    public ExpectedException exception;
    @Rule
    public ExpectedException expectedException;

    @Test
    public void testPrintHelp() {

        try {
            Flags flags = new Flags()
            .loadOpts(FlagsHelpTest.class);
            flags.printHelp(System.out);
        } catch (Exception e) {
            assertFalse("Cought exception.", true);
        }
    }

}