private void generateGroupDecoderClassHeader(
        final StringBuilder sb,
        final String groupName,
        final String parentMessageClassName,
        final FieldPrecedenceModel fieldPrecedenceModel,
        final Token groupToken,
        final List<Token> tokens,
        final List<Token> subGroupTokens,
        final int index,
        final String indent)
    {
        final String className = formatClassName(groupName);
        final int dimensionHeaderLen = tokens.get(index + 1).encodedLength();

        final Token blockLengthToken = Generators.findFirst("blockLength", tokens, index);
        final Token numInGroupToken = Generators.findFirst("numInGroup", tokens, index);

        final PrimitiveType blockLengthType = blockLengthToken.encoding().primitiveType();
        final String blockLengthOffset = "limit + " + blockLengthToken.offset();
        final String blockLengthGet = generateGet(
            blockLengthType, blockLengthOffset, byteOrderString(blockLengthToken.encoding()));

        final PrimitiveType numInGroupType = numInGroupToken.encoding().primitiveType();
        final String numInGroupOffset = "limit + " + numInGroupToken.offset();
        final String numInGroupGet = generateGet(
            numInGroupType, numInGroupOffset, byteOrderString(numInGroupToken.encoding()));

        generateGroupDecoderClassDeclaration(
            sb,
            groupName,
            parentMessageClassName,
            findSubGroupNames(subGroupTokens),
            indent,
            dimensionHeaderLen);

        final String blockLenCast = PrimitiveType.UINT32 == blockLengthType ? "(int)" : "";
        final String numInGroupCast = PrimitiveType.UINT32 == numInGroupType ? "(int)" : "";

        sb.append("\n")
            .append(indent).append("    public void wrap(final ").append(readOnlyBuffer).append(" buffer)\n")
            .append(indent).append("    {\n")
            .append(indent).append("        if (buffer != this.buffer)\n")
            .append(indent).append("        {\n")
            .append(indent).append("            this.buffer = buffer;\n")
            .append(indent).append("        }\n\n")
            .append(indent).append("        index = 0;\n")
            .append(indent).append("        final int limit = parentMessage.limit();\n")
            .append(indent).append("        parentMessage.limit(limit + HEADER_SIZE);\n")
            .append(indent).append("        blockLength = ").append(blockLenCast).append(blockLengthGet).append(";\n")
            .append(indent).append("        count = ").append(numInGroupCast).append(numInGroupGet).append(";\n")
            .append(indent).append("    }\n\n");

        generateAccessOrderListenerMethodForNextGroupElement(sb, fieldPrecedenceModel, indent + "    ", groupToken);

        sb.append(indent).append("    public ").append(className).append(" next()\n")
            .append(indent).append("    {\n")
            .append(indent).append("        if (index >= count)\n")
            .append(indent).append("        {\n")
            .append(indent).append("            throw new java.util.NoSuchElementException();\n")
            .append(indent).append("        }\n\n")
            .append(generateAccessOrderListenerCall(fieldPrecedenceModel, indent + "        ", "onNextElementAccessed"))
            .append(indent).append("        offset = parentMessage.limit();\n")
            .append(indent).append("        parentMessage.limit(offset + blockLength);\n")
            .append(indent).append("        ++index;\n\n")
            .append(indent).append("        return this;\n")
            .append(indent).append("    }\n");

        final String numInGroupJavaTypeName = javaTypeName(numInGroupType);
        final String numInGroupMinValue = generateLiteral(
            numInGroupType, numInGroupToken.encoding().applicableMinValue().toString());
        generatePrimitiveFieldMetaMethod(sb, indent, numInGroupJavaTypeName, "count", "Min", numInGroupMinValue);
        final String numInGroupMaxValue = generateLiteral(
            numInGroupType, numInGroupToken.encoding().applicableMaxValue().toString());
        generatePrimitiveFieldMetaMethod(sb, indent, numInGroupJavaTypeName, "count", "Max", numInGroupMaxValue);

        sb.append("\n")
            .append(indent).append("    public static int sbeHeaderSize()\n")
            .append(indent).append("    {\n")
            .append(indent).append("        return HEADER_SIZE;\n")
            .append(indent).append("    }\n");

        sb.append("\n")
            .append(indent).append("    public static int sbeBlockLength()\n")
            .append(indent).append("    {\n")
            .append(indent).append("        return ").append(tokens.get(index).encodedLength()).append(";\n")
            .append(indent).append("    }\n");

        sb.append("\n")
            .append(indent).append("    public int actingBlockLength()\n")
            .append(indent).append("    {\n")
            .append(indent).append("        return blockLength;\n")
            .append(indent).append("    }\n\n")
            .append(indent).append("    public int actingVersion()\n")
            .append(indent).append("    {\n")
            .append(indent).append("        return parentMessage.actingVersion;\n")
            .append(indent).append("    }\n\n")
            .append(indent).append("    public int count()\n")
            .append(indent).append("    {\n")
            .append(indent).append("        return count;\n")
            .append(indent).append("    }\n\n")
            .append(indent).append("    public java.util.Iterator<").append(className).append("> iterator()\n")
            .append(indent).append("    {\n")
            .append(indent).append("        return this;\n")
            .append(indent).append("    }\n\n")
            .append(indent).append("    public void remove()\n")
            .append(indent).append("    {\n")
            .append(indent).append("        throw new UnsupportedOperationException();\n")
            .append(indent).append("    }\n\n")
            .append(indent).append("    public boolean hasNext()\n")
            .append(indent).append("    {\n")
            .append(indent).append("        return index < count;\n")
            .append(indent).append("    }\n");

        if (null != fieldPrecedenceModel)
        {
            sb.append("\n")
                .append(indent).append("    private int codecState()\n")
                .append(indent).append("    {\n")
                .append(indent).append("        return parentMessage.codecState();\n")
                .append(indent).append("    }\n");

            sb.append("\n")
                .append(indent).append("    private void codecState(final int newState)\n")
                .append(indent).append("    {\n")
                .append(indent).append("        parentMessage.codecState(newState);\n")
                .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