private void generateDecoderGroups(
        final StringBuilder sb,
        final FieldPrecedenceModel fieldPrecedenceModel,
        final String outerClassName,
        final List<Token> tokens,
        final String indent,
        final boolean isSubGroup) throws IOException
    {
        for (int i = 0, size = tokens.size(); i < size; i++)
        {
            final Token groupToken = tokens.get(i);
            if (groupToken.signal() != Signal.BEGIN_GROUP)
            {
                throw new IllegalStateException("tokens must begin with BEGIN_GROUP: token=" + groupToken);
            }

            final int index = i;
            final String groupName = decoderName(formatClassName(groupToken.name()));

            ++i;
            final int groupHeaderTokenCount = tokens.get(i).componentTokenCount();
            i += groupHeaderTokenCount;

            final List<Token> fields = new ArrayList<>();
            i = collectFields(tokens, i, fields);

            final List<Token> groups = new ArrayList<>();
            i = collectGroups(tokens, i, groups);

            final List<Token> varData = new ArrayList<>();
            i = collectVarData(tokens, i, varData);

            generateGroupDecoderProperty(sb, groupName, fieldPrecedenceModel, groupToken, indent, isSubGroup);
            generateTypeJavadoc(sb, indent + INDENT, groupToken);

            if (shouldGenerateGroupOrderAnnotation)
            {
                generateAnnotations(indent + INDENT, groupName, groups, sb, JavaUtil::decoderName);
            }
            generateGroupDecoderClassHeader(sb, groupName, outerClassName, fieldPrecedenceModel, groupToken,
                tokens, groups, index, indent + INDENT);

            generateDecoderFields(sb, fieldPrecedenceModel, fields, indent + INDENT);
            generateDecoderGroups(sb, fieldPrecedenceModel, outerClassName, groups, indent + INDENT, true);
            generateDecoderVarData(sb, fieldPrecedenceModel, varData, indent + INDENT);

            appendGroupInstanceDecoderDisplay(sb, fields, groups, varData, indent + INDENT);
            generateMessageLength(sb, groupName, false, groups, varData, indent + INDENT);

            sb.append(indent).append("    }\n");
        }
    }
\n");
        sb.append("     * The states in which a encoder/decoder/codec can live.\n");
        sb.append("     *\n");
        sb.append("     * <p>The state machine diagram below, encoded in the dot language, describes\n");
        sb.append("     * the valid state transitions according to the order in which fields may be\n");
        sb.append("     * accessed safely. Tools such as PlantUML and Graphviz can render it.\n");
        sb.append("     *\n");
        sb.append("     * <pre>{@code\n");
        fieldPrecedenceModel.generateGraph(sb, "     *   ");
        sb.append("     * }</pre>\n");
        sb.append("
sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java