uk.co.real_logic.sbe.generation.java.JavaGenerator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sbe-all Show documentation
Show all versions of sbe-all Show documentation
FIX/SBE - OSI layer 6 presentation for encoding and decoding application messages in binary format for low-latency applications.
/*
* Copyright 2013-2023 Real Logic Limited.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package uk.co.real_logic.sbe.generation.java;
import org.agrona.DirectBuffer;
import org.agrona.MutableDirectBuffer;
import org.agrona.Strings;
import org.agrona.Verify;
import org.agrona.generation.DynamicPackageOutputManager;
import org.agrona.sbe.*;
import uk.co.real_logic.sbe.PrimitiveType;
import uk.co.real_logic.sbe.generation.CodeGenerator;
import uk.co.real_logic.sbe.generation.Generators;
import uk.co.real_logic.sbe.ir.*;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Formatter;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import static uk.co.real_logic.sbe.SbeTool.JAVA_INTERFACE_PACKAGE;
import static uk.co.real_logic.sbe.generation.java.JavaGenerator.CodecType.DECODER;
import static uk.co.real_logic.sbe.generation.java.JavaGenerator.CodecType.ENCODER;
import static uk.co.real_logic.sbe.generation.java.JavaUtil.*;
import static uk.co.real_logic.sbe.ir.GenerationUtil.*;
/**
* Generate codecs for the Java 8 programming language.
*/
@SuppressWarnings("MethodLength")
public class JavaGenerator implements CodeGenerator
{
static final String MESSAGE_HEADER_ENCODER_TYPE = "MessageHeaderEncoder";
static final String MESSAGE_HEADER_DECODER_TYPE = "MessageHeaderDecoder";
enum CodecType
{
DECODER,
ENCODER
}
private static final String META_ATTRIBUTE_ENUM = "MetaAttribute";
private static final String PACKAGE_INFO = "package-info";
private static final String BASE_INDENT = "";
private static final String INDENT = " ";
private static final Set PACKAGES_EMPTY_SET = Collections.emptySet();
private final Ir ir;
private final DynamicPackageOutputManager outputManager;
private final String fqMutableBuffer;
private final String mutableBuffer;
private final String fqReadOnlyBuffer;
private final String readOnlyBuffer;
private final boolean shouldGenerateGroupOrderAnnotation;
private final boolean shouldGenerateInterfaces;
private final boolean shouldDecodeUnknownEnumValues;
private final boolean shouldSupportTypesPackageNames;
private final Set packageNameByTypes = new HashSet<>();
/**
* Create a new Java language {@link CodeGenerator}. Generator support for types in their own package is disabled.
*
* @param ir for the messages and types.
* @param mutableBuffer implementation used for mutating underlying buffers.
* @param readOnlyBuffer implementation used for reading underlying buffers.
* @param shouldGenerateGroupOrderAnnotation in the codecs.
* @param shouldGenerateInterfaces for common methods.
* @param shouldDecodeUnknownEnumValues generate support for unknown enum values when decoding.
* @param outputManager for generating the codecs to.
*/
public JavaGenerator(
final Ir ir,
final String mutableBuffer,
final String readOnlyBuffer,
final boolean shouldGenerateGroupOrderAnnotation,
final boolean shouldGenerateInterfaces,
final boolean shouldDecodeUnknownEnumValues,
final DynamicPackageOutputManager outputManager)
{
this(ir, mutableBuffer, readOnlyBuffer, shouldGenerateGroupOrderAnnotation, shouldGenerateInterfaces,
shouldDecodeUnknownEnumValues, false, outputManager);
}
/**
* Create a new Java language {@link CodeGenerator}.
*
* @param ir for the messages and types.
* @param mutableBuffer implementation used for mutating underlying buffers.
* @param readOnlyBuffer implementation used for reading underlying buffers.
* @param shouldGenerateGroupOrderAnnotation in the codecs.
* @param shouldGenerateInterfaces for common methods.
* @param shouldDecodeUnknownEnumValues generate support for unknown enum values when decoding.
* @param shouldSupportTypesPackageNames generator support for types in their own package.
* @param outputManager for generating the codecs to.
*/
public JavaGenerator(
final Ir ir,
final String mutableBuffer,
final String readOnlyBuffer,
final boolean shouldGenerateGroupOrderAnnotation,
final boolean shouldGenerateInterfaces,
final boolean shouldDecodeUnknownEnumValues,
final boolean shouldSupportTypesPackageNames,
final DynamicPackageOutputManager outputManager)
{
Verify.notNull(ir, "ir");
Verify.notNull(outputManager, "outputManager");
this.ir = ir;
this.shouldSupportTypesPackageNames = shouldSupportTypesPackageNames;
this.outputManager = outputManager;
this.mutableBuffer = validateBufferImplementation(mutableBuffer, MutableDirectBuffer.class);
this.fqMutableBuffer = mutableBuffer;
this.readOnlyBuffer = validateBufferImplementation(readOnlyBuffer, DirectBuffer.class);
this.fqReadOnlyBuffer = readOnlyBuffer;
this.shouldGenerateGroupOrderAnnotation = shouldGenerateGroupOrderAnnotation;
this.shouldGenerateInterfaces = shouldGenerateInterfaces;
this.shouldDecodeUnknownEnumValues = shouldDecodeUnknownEnumValues;
}
/**
* Generate the composites for dealing with the message header.
*
* @throws IOException if an error is encountered when writing the output.
*/
public void generateMessageHeaderStub() throws IOException
{
generateComposite(ir.headerStructure().tokens());
}
/**
* Generate the stubs for the types used as message fields.
*
* @throws IOException if an error is encountered when writing the output.
*/
public void generateTypeStubs() throws IOException
{
generateMetaAttributeEnum();
for (final List tokens : ir.types())
{
switch (tokens.get(0).signal())
{
case BEGIN_ENUM:
generateEnum(tokens);
break;
case BEGIN_SET:
generateBitSet(tokens);
break;
case BEGIN_COMPOSITE:
generateComposite(tokens);
break;
default:
break;
}
}
}
/**
* Register the types explicit package - if set and should be supported.
*
* @param token the 0-th token of the type.
* @param ir the intermediate representation.
* @return the overridden package name of the type if set and supported, or {@link Ir#applicableNamespace()}.
*/
private String registerTypesPackageName(final Token token, final Ir ir)
{
if (!shouldSupportTypesPackageNames)
{
return ir.applicableNamespace();
}
if (token.packageName() != null)
{
packageNameByTypes.add(token.packageName());
outputManager.setPackageName(token.packageName());
return token.packageName();
}
return ir.applicableNamespace();
}
/**
* {@inheritDoc}
*/
public void generate() throws IOException
{
packageNameByTypes.clear();
generatePackageInfo();
generateTypeStubs();
generateMessageHeaderStub();
for (final List tokens : ir.messages())
{
final Token msgToken = tokens.get(0);
final List messageBody = getMessageBody(tokens);
final boolean hasVarData = -1 != findSignal(messageBody, Signal.BEGIN_VAR_DATA);
int i = 0;
final List fields = new ArrayList<>();
i = collectFields(messageBody, i, fields);
final List groups = new ArrayList<>();
i = collectGroups(messageBody, i, groups);
final List varData = new ArrayList<>();
collectVarData(messageBody, i, varData);
generateDecoder(msgToken, fields, groups, varData, hasVarData);
generateEncoder(msgToken, fields, groups, varData, hasVarData);
}
}
private void generateEncoder(
final Token msgToken,
final List fields,
final List groups,
final List varData,
final boolean hasVarData)
throws IOException
{
final String className = formatClassName(encoderName(msgToken.name()));
final String implementsString = implementsInterface(MessageEncoderFlyweight.class.getSimpleName());
try (Writer out = outputManager.createOutput(className))
{
out.append(generateMainHeader(ir.applicableNamespace(), ENCODER, hasVarData));
if (shouldGenerateGroupOrderAnnotation)
{
generateAnnotations(BASE_INDENT, className, groups, out, this::encoderName);
}
out.append(generateDeclaration(className, implementsString, msgToken));
out.append(generateEncoderFlyweightCode(className, msgToken));
final StringBuilder sb = new StringBuilder();
generateEncoderFields(sb, className, fields, BASE_INDENT);
generateEncoderGroups(sb, className, groups, BASE_INDENT, false);
generateEncoderVarData(sb, className, varData, BASE_INDENT);
generateEncoderDisplay(sb, decoderName(msgToken.name()));
out.append(sb);
out.append("}\n");
}
}
private void generateDecoder(
final Token msgToken,
final List fields,
final List groups,
final List varData,
final boolean hasVarData)
throws IOException
{
final String className = formatClassName(decoderName(msgToken.name()));
final String implementsString = implementsInterface(MessageDecoderFlyweight.class.getSimpleName());
try (Writer out = outputManager.createOutput(className))
{
out.append(generateMainHeader(ir.applicableNamespace(), DECODER, hasVarData));
if (shouldGenerateGroupOrderAnnotation)
{
generateAnnotations(BASE_INDENT, className, groups, out, this::decoderName);
}
out.append(generateDeclaration(className, implementsString, msgToken));
out.append(generateDecoderFlyweightCode(className, msgToken));
final StringBuilder sb = new StringBuilder();
generateDecoderFields(sb, fields, BASE_INDENT);
generateDecoderGroups(sb, className, groups, BASE_INDENT, false);
generateDecoderVarData(sb, varData, BASE_INDENT);
generateDecoderDisplay(sb, msgToken.name(), fields, groups, varData);
generateMessageLength(sb, className, true, groups, varData, BASE_INDENT);
out.append(sb);
out.append("}\n");
}
}
private void generateDecoderGroups(
final StringBuilder sb,
final String outerClassName,
final List 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 fields = new ArrayList<>();
i = collectFields(tokens, i, fields);
final List groups = new ArrayList<>();
i = collectGroups(tokens, i, groups);
final List varData = new ArrayList<>();
i = collectVarData(tokens, i, varData);
generateGroupDecoderProperty(sb, groupName, groupToken, indent, isSubGroup);
generateTypeJavadoc(sb, indent + INDENT, groupToken);
if (shouldGenerateGroupOrderAnnotation)
{
generateAnnotations(indent + INDENT, groupName, groups, sb, this::decoderName);
}
generateGroupDecoderClassHeader(sb, groupName, outerClassName, tokens, groups, index, indent + INDENT);
generateDecoderFields(sb, fields, indent + INDENT);
generateDecoderGroups(sb, outerClassName, groups, indent + INDENT, true);
generateDecoderVarData(sb, varData, indent + INDENT);
appendGroupInstanceDecoderDisplay(sb, fields, groups, varData, indent + INDENT);
generateMessageLength(sb, groupName, false, groups, varData, indent + INDENT);
sb.append(indent).append(" }\n");
}
}
private void generateEncoderGroups(
final StringBuilder sb,
final String outerClassName,
final List 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 = groupToken.name();
final String groupClassName = encoderName(groupName);
++i;
final int groupHeaderTokenCount = tokens.get(i).componentTokenCount();
i += groupHeaderTokenCount;
final List fields = new ArrayList<>();
i = collectFields(tokens, i, fields);
final List groups = new ArrayList<>();
i = collectGroups(tokens, i, groups);
final List varData = new ArrayList<>();
i = collectVarData(tokens, i, varData);
generateGroupEncoderProperty(sb, groupName, groupToken, indent, isSubGroup);
generateTypeJavadoc(sb, indent + INDENT, groupToken);
if (shouldGenerateGroupOrderAnnotation)
{
generateAnnotations(indent + INDENT, groupClassName, groups, sb, this::encoderName);
}
generateGroupEncoderClassHeader(sb, groupName, outerClassName, tokens, groups, index, indent + INDENT);
generateEncoderFields(sb, groupClassName, fields, indent + INDENT);
generateEncoderGroups(sb, outerClassName, groups, indent + INDENT, true);
generateEncoderVarData(sb, groupClassName, varData, indent + INDENT);
sb.append(indent).append(" }\n");
}
}
private void generateGroupDecoderClassHeader(
final StringBuilder sb,
final String groupName,
final String parentMessageClassName,
final List tokens,
final List 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");
sb.append("\n")
.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(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 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");
}
private void generateGroupEncoderClassHeader(
final StringBuilder sb,
final String groupName,
final String parentMessageClassName,
final List tokens,
final List subGroupTokens,
final int index,
final String ind)
{
final int dimensionHeaderSize = tokens.get(index + 1).encodedLength();
generateGroupEncoderClassDeclaration(
sb,
groupName,
parentMessageClassName,
findSubGroupNames(subGroupTokens),
ind,
dimensionHeaderSize);
final int blockLength = tokens.get(index).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 blockLengthValue = Integer.toString(blockLength);
final String blockLengthPut = generatePut(
blockLengthType, blockLengthOffset, blockLengthValue, byteOrderString(blockLengthToken.encoding()));
final PrimitiveType numInGroupType = numInGroupToken.encoding().primitiveType();
final PrimitiveType numInGroupTypeCast = PrimitiveType.UINT32 == numInGroupType ?
PrimitiveType.INT32 : numInGroupType;
final String numInGroupOffset = "limit + " + numInGroupToken.offset();
final String numInGroupValue = "count";
final String numInGroupPut = generatePut(
numInGroupTypeCast, numInGroupOffset, numInGroupValue, byteOrderString(numInGroupToken.encoding()));
new Formatter(sb).format("\n" +
ind + " public void wrap(final %2$s buffer, final int count)\n" +
ind + " {\n" +
ind + " if (count < %3$d || count > %4$d)\n" +
ind + " {\n" +
ind + " throw new IllegalArgumentException(\"count outside allowed range: count=\" + count);\n" +
ind + " }\n\n" +
ind + " if (buffer != this.buffer)\n" +
ind + " {\n" +
ind + " this.buffer = buffer;\n" +
ind + " }\n\n" +
ind + " index = 0;\n" +
ind + " this.count = count;\n" +
ind + " final int limit = parentMessage.limit();\n" +
ind + " initialLimit = limit;\n" +
ind + " parentMessage.limit(limit + HEADER_SIZE);\n" +
ind + " %5$s;\n" +
ind + " %6$s;\n" +
ind + " }\n",
parentMessageClassName,
mutableBuffer,
numInGroupToken.encoding().applicableMinValue().longValue(),
numInGroupToken.encoding().applicableMaxValue().longValue(),
blockLengthPut,
numInGroupPut);
sb.append("\n")
.append(ind).append(" public ").append(encoderName(groupName)).append(" next()\n")
.append(ind).append(" {\n")
.append(ind).append(" if (index >= count)\n")
.append(ind).append(" {\n")
.append(ind).append(" throw new java.util.NoSuchElementException();\n")
.append(ind).append(" }\n\n")
.append(ind).append(" offset = parentMessage.limit();\n")
.append(ind).append(" parentMessage.limit(offset + sbeBlockLength());\n")
.append(ind).append(" ++index;\n\n")
.append(ind).append(" return this;\n")
.append(ind).append(" }\n");
final String countOffset = "initialLimit + " + numInGroupToken.offset();
final String resetCountPut = generatePut(
numInGroupTypeCast, countOffset, numInGroupValue, byteOrderString(numInGroupToken.encoding()));
sb.append("\n")
.append(ind).append(" public int resetCountToIndex()\n")
.append(ind).append(" {\n")
.append(ind).append(" count = index;\n")
.append(ind).append(" ").append(resetCountPut).append(";\n\n")
.append(ind).append(" return count;\n")
.append(ind).append(" }\n");
final String numInGroupJavaTypeName = javaTypeName(numInGroupType);
final String numInGroupMinValue = generateLiteral(
numInGroupType, numInGroupToken.encoding().applicableMinValue().toString());
generatePrimitiveFieldMetaMethod(sb, ind, numInGroupJavaTypeName, "count", "Min", numInGroupMinValue);
final String numInGroupMaxValue = generateLiteral(
numInGroupType, numInGroupToken.encoding().applicableMaxValue().toString());
generatePrimitiveFieldMetaMethod(sb, ind, numInGroupJavaTypeName, "count", "Max", numInGroupMaxValue);
sb.append("\n")
.append(ind).append(" public static int sbeHeaderSize()\n")
.append(ind).append(" {\n")
.append(ind).append(" return HEADER_SIZE;\n")
.append(ind).append(" }\n");
sb.append("\n")
.append(ind).append(" public static int sbeBlockLength()\n")
.append(ind).append(" {\n")
.append(ind).append(" return ").append(blockLength).append(";\n")
.append(ind).append(" }\n");
}
private static String primitiveTypeName(final Token token)
{
return javaTypeName(token.encoding().primitiveType());
}
private void generateGroupDecoderClassDeclaration(
final StringBuilder sb,
final String groupName,
final String parentMessageClassName,
final List subGroupNames,
final String indent,
final int dimensionHeaderSize)
{
final String className = formatClassName(groupName);
new Formatter(sb).format("\n" +
indent + "public static final class %1$s\n" +
indent + " implements Iterable<%1$s>, java.util.Iterator<%1$s>\n" +
indent + "{\n" +
indent + " public static final int HEADER_SIZE = %2$d;\n" +
indent + " private final %3$s parentMessage;\n" +
indent + " private %4$s buffer;\n" +
indent + " private int count;\n" +
indent + " private int index;\n" +
indent + " private int offset;\n" +
indent + " private int blockLength;\n",
className,
dimensionHeaderSize,
parentMessageClassName,
readOnlyBuffer);
for (final String subGroupName : subGroupNames)
{
final String type = formatClassName(decoderName(subGroupName));
final String field = formatPropertyName(subGroupName);
sb.append(indent).append(" private final ").append(type).append(" ").append(field).append(";\n");
}
sb
.append("\n")
.append(indent).append(" ")
.append(className).append("(final ").append(parentMessageClassName).append(" parentMessage)\n")
.append(indent).append(" {\n")
.append(indent).append(" this.parentMessage = parentMessage;\n");
for (final String subGroupName : subGroupNames)
{
final String type = formatClassName(decoderName(subGroupName));
final String field = formatPropertyName(subGroupName);
sb
.append(indent).append(" ")
.append(field).append(" = new ").append(type).append("(parentMessage);\n");
}
sb.append(indent).append(" }\n");
}
private void generateGroupEncoderClassDeclaration(
final StringBuilder sb,
final String groupName,
final String parentMessageClassName,
final List subGroupNames,
final String indent,
final int dimensionHeaderSize)
{
final String className = encoderName(groupName);
new Formatter(sb).format("\n" +
indent + "public static final class %1$s\n" +
indent + "{\n" +
indent + " public static final int HEADER_SIZE = %2$d;\n" +
indent + " private final %3$s parentMessage;\n" +
indent + " private %4$s buffer;\n" +
indent + " private int count;\n" +
indent + " private int index;\n" +
indent + " private int offset;\n" +
indent + " private int initialLimit;\n",
className,
dimensionHeaderSize,
parentMessageClassName,
mutableBuffer);
for (final String subGroupName : subGroupNames)
{
final String type = encoderName(subGroupName);
final String field = formatPropertyName(subGroupName);
sb.append(indent).append(" private final ").append(type).append(" ").append(field).append(";\n");
}
sb
.append("\n")
.append(indent).append(" ")
.append(className).append("(final ").append(parentMessageClassName).append(" parentMessage)\n")
.append(indent).append(" {\n")
.append(indent).append(" this.parentMessage = parentMessage;\n");
for (final String subGroupName : subGroupNames)
{
final String type = encoderName(subGroupName);
final String field = formatPropertyName(subGroupName);
sb
.append(indent).append(" ")
.append(field).append(" = new ").append(type).append("(parentMessage);\n");
}
sb.append(indent).append(" }\n");
}
private static void generateGroupDecoderProperty(
final StringBuilder sb,
final String groupName,
final Token token,
final String indent,
final boolean isSubGroup)
{
final String className = formatClassName(groupName);
final String propertyName = formatPropertyName(token.name());
if (!isSubGroup)
{
new Formatter(sb).format("\n" +
indent + " private final %s %s = new %s(this);\n",
className,
propertyName,
className);
}
new Formatter(sb).format("\n" +
indent + " public static long %sId()\n" +
indent + " {\n" +
indent + " return %d;\n" +
indent + " }\n",
formatPropertyName(groupName),
token.id());
new Formatter(sb).format("\n" +
indent + " public static int %sSinceVersion()\n" +
indent + " {\n" +
indent + " return %d;\n" +
indent + " }\n",
formatPropertyName(groupName),
token.version());
final String actingVersionGuard = token.version() == 0 ?
"" :
indent + " if (parentMessage.actingVersion < " + token.version() + ")\n" +
indent + " {\n" +
indent + " " + propertyName + ".count = 0;\n" +
indent + " " + propertyName + ".index = 0;\n" +
indent + " return " + propertyName + ";\n" +
indent + " }\n\n";
generateFlyweightPropertyJavadoc(sb, indent + INDENT, token, className);
new Formatter(sb).format("\n" +
indent + " public %1$s %2$s()\n" +
indent + " {\n" +
"%3$s" +
indent + " %2$s.wrap(buffer);\n" +
indent + " return %2$s;\n" +
indent + " }\n",
className,
propertyName,
actingVersionGuard);
}
private void generateGroupEncoderProperty(
final StringBuilder sb,
final String groupName,
final Token token,
final String indent,
final boolean isSubGroup)
{
final String className = formatClassName(encoderName(groupName));
final String propertyName = formatPropertyName(groupName);
if (!isSubGroup)
{
new Formatter(sb).format("\n" +
indent + " private final %s %s = new %s(this);\n",
className,
propertyName,
className);
}
new Formatter(sb).format("\n" +
indent + " public static long %sId()\n" +
indent + " {\n" +
indent + " return %d;\n" +
indent + " }\n",
formatPropertyName(groupName),
token.id());
generateGroupEncodePropertyJavadoc(sb, indent + INDENT, token, className);
new Formatter(sb).format("\n" +
indent + " public %1$s %2$sCount(final int count)\n" +
indent + " {\n" +
indent + " %2$s.wrap(buffer, count);\n" +
indent + " return %2$s;\n" +
indent + " }\n",
className,
propertyName);
}
private void generateDecoderVarData(
final StringBuilder sb, final List tokens, final String indent)
{
for (int i = 0, size = tokens.size(); i < size;)
{
final Token token = tokens.get(i);
if (token.signal() != Signal.BEGIN_VAR_DATA)
{
throw new IllegalStateException("tokens must begin with BEGIN_VAR_DATA: token=" + token);
}
generateFieldIdMethod(sb, token, indent);
generateFieldSinceVersionMethod(sb, token, indent);
final String characterEncoding = tokens.get(i + 3).encoding().characterEncoding();
generateCharacterEncodingMethod(sb, token.name(), characterEncoding, indent);
generateFieldMetaAttributeMethod(sb, token, indent);
final String propertyName = Generators.toUpperFirstChar(token.name());
final Token lengthToken = tokens.get(i + 2);
final int sizeOfLengthField = lengthToken.encodedLength();
final Encoding lengthEncoding = lengthToken.encoding();
final PrimitiveType lengthType = lengthEncoding.primitiveType();
final String byteOrderStr = byteOrderString(lengthEncoding);
final String methodPropName = Generators.toLowerFirstChar(propertyName);
sb.append("\n")
.append(indent).append(" public static int ").append(methodPropName).append("HeaderLength()\n")
.append(indent).append(" {\n")
.append(indent).append(" return ").append(sizeOfLengthField).append(";\n")
.append(indent).append(" }\n");
sb.append("\n")
.append(indent).append(" public int ").append(methodPropName).append("Length()\n")
.append(indent).append(" {\n")
.append(generateArrayFieldNotPresentCondition(token.version(), indent))
.append(indent).append(" final int limit = parentMessage.limit();\n")
.append(indent).append(" return ").append(PrimitiveType.UINT32 == lengthType ? "(int)" : "")
.append(generateGet(lengthType, "limit", byteOrderStr)).append(";\n")
.append(indent).append(" }\n");
generateDataDecodeMethods(
sb, token, propertyName, sizeOfLengthField, lengthType, byteOrderStr, characterEncoding, indent);
i += token.componentTokenCount();
}
}
private void generateEncoderVarData(
final StringBuilder sb, final String className, final List tokens, final String indent)
{
for (int i = 0, size = tokens.size(); i < size;)
{
final Token token = tokens.get(i);
if (token.signal() != Signal.BEGIN_VAR_DATA)
{
throw new IllegalStateException("tokens must begin with BEGIN_VAR_DATA: token=" + token);
}
generateFieldIdMethod(sb, token, indent);
final Token varDataToken = Generators.findFirst("varData", tokens, i);
final String characterEncoding = varDataToken.encoding().characterEncoding();
generateCharacterEncodingMethod(sb, token.name(), characterEncoding, indent);
generateFieldMetaAttributeMethod(sb, token, indent);
final String propertyName = Generators.toUpperFirstChar(token.name());
final Token lengthToken = Generators.findFirst("length", tokens, i);
final int sizeOfLengthField = lengthToken.encodedLength();
final Encoding lengthEncoding = lengthToken.encoding();
final int maxLengthValue = (int)lengthEncoding.applicableMaxValue().longValue();
final String byteOrderStr = byteOrderString(lengthEncoding);
final String methodPropName = Generators.toLowerFirstChar(propertyName);
sb.append("\n")
.append(indent).append(" public static int ").append(methodPropName).append("HeaderLength()\n")
.append(indent).append(" {\n")
.append(indent).append(" return ")
.append(sizeOfLengthField).append(";\n")
.append(indent).append(" }\n");
generateDataEncodeMethods(
sb,
propertyName,
sizeOfLengthField,
maxLengthValue,
lengthEncoding.primitiveType(),
byteOrderStr,
characterEncoding,
className,
indent);
i += token.componentTokenCount();
}
}
private void generateDataDecodeMethods(
final StringBuilder sb,
final Token token,
final String propertyName,
final int sizeOfLengthField,
final PrimitiveType lengthType,
final String byteOrderStr,
final String characterEncoding,
final String indent)
{
new Formatter(sb).format("\n" +
indent + " public int skip%1$s()\n" +
indent + " {\n" +
"%2$s" +
indent + " final int headerLength = %3$d;\n" +
indent + " final int limit = parentMessage.limit();\n" +
indent + " final int dataLength = %4$s%5$s;\n" +
indent + " final int dataOffset = limit + headerLength;\n" +
indent + " parentMessage.limit(dataOffset + dataLength);\n\n" +
indent + " return dataLength;\n" +
indent + " }\n",
Generators.toUpperFirstChar(propertyName),
generateStringNotPresentConditionForAppendable(token.version(), indent),
sizeOfLengthField,
PrimitiveType.UINT32 == lengthType ? "(int)" : "",
generateGet(lengthType, "limit", byteOrderStr));
generateVarDataTypedDecoder(
sb,
token,
propertyName,
sizeOfLengthField,
mutableBuffer,
lengthType,
byteOrderStr,
indent);
generateVarDataTypedDecoder(
sb,
token,
propertyName,
sizeOfLengthField,
"byte[]",
lengthType,
byteOrderStr,
indent);
generateVarDataWrapDecoder(sb, token, propertyName, sizeOfLengthField, lengthType, byteOrderStr, indent);
if (null != characterEncoding)
{
new Formatter(sb).format("\n" +
indent + " public String %1$s()\n" +
indent + " {\n" +
"%2$s" +
indent + " final int headerLength = %3$d;\n" +
indent + " final int limit = parentMessage.limit();\n" +
indent + " final int dataLength = %4$s%5$s;\n" +
indent + " parentMessage.limit(limit + headerLength + dataLength);\n\n" +
indent + " if (0 == dataLength)\n" +
indent + " {\n" +
indent + " return \"\";\n" +
indent + " }\n\n" +
indent + " final byte[] tmp = new byte[dataLength];\n" +
indent + " buffer.getBytes(limit + headerLength, tmp, 0, dataLength);\n\n" +
indent + " return new String(tmp, %6$s);\n" +
indent + " }\n",
formatPropertyName(propertyName),
generateStringNotPresentCondition(token.version(), indent),
sizeOfLengthField,
PrimitiveType.UINT32 == lengthType ? "(int)" : "",
generateGet(lengthType, "limit", byteOrderStr),
charset(characterEncoding));
if (isAsciiEncoding(characterEncoding))
{
new Formatter(sb).format("\n" +
indent + " public int get%1$s(final Appendable appendable)\n" +
indent + " {\n" +
"%2$s" +
indent + " final int headerLength = %3$d;\n" +
indent + " final int limit = parentMessage.limit();\n" +
indent + " final int dataLength = %4$s%5$s;\n" +
indent + " final int dataOffset = limit + headerLength;\n\n" +
indent + " parentMessage.limit(dataOffset + dataLength);\n" +
indent + " buffer.getStringWithoutLengthAscii(dataOffset, dataLength, appendable);\n\n" +
indent + " return dataLength;\n" +
indent + " }\n",
Generators.toUpperFirstChar(propertyName),
generateStringNotPresentConditionForAppendable(token.version(), indent),
sizeOfLengthField,
PrimitiveType.UINT32 == lengthType ? "(int)" : "",
generateGet(lengthType, "limit", byteOrderStr));
}
}
}
private void generateVarDataWrapDecoder(
final StringBuilder sb,
final Token token,
final String propertyName,
final int sizeOfLengthField,
final PrimitiveType lengthType,
final String byteOrderStr,
final String indent)
{
new Formatter(sb).format("\n" +
indent + " public void wrap%s(final %s wrapBuffer)\n" +
indent + " {\n" +
"%s" +
indent + " final int headerLength = %d;\n" +
indent + " final int limit = parentMessage.limit();\n" +
indent + " final int dataLength = %s%s;\n" +
indent + " parentMessage.limit(limit + headerLength + dataLength);\n" +
indent + " wrapBuffer.wrap(buffer, limit + headerLength, dataLength);\n" +
indent + " }\n",
propertyName,
readOnlyBuffer,
generateWrapFieldNotPresentCondition(token.version(), indent),
sizeOfLengthField,
PrimitiveType.UINT32 == lengthType ? "(int)" : "",
generateGet(lengthType, "limit", byteOrderStr));
}
private void generateDataEncodeMethods(
final StringBuilder sb,
final String propertyName,
final int sizeOfLengthField,
final int maxLengthValue,
final PrimitiveType lengthType,
final String byteOrderStr,
final String characterEncoding,
final String className,
final String indent)
{
generateDataTypedEncoder(
sb,
className,
propertyName,
sizeOfLengthField,
maxLengthValue,
readOnlyBuffer,
lengthType,
byteOrderStr,
indent);
generateDataTypedEncoder(
sb,
className,
propertyName,
sizeOfLengthField,
maxLengthValue,
"byte[]",
lengthType,
byteOrderStr,
indent);
if (null != characterEncoding)
{
generateCharArrayEncodeMethods(
sb,
propertyName,
sizeOfLengthField,
maxLengthValue,
lengthType,
byteOrderStr,
characterEncoding,
className,
indent);
}
}
private void generateCharArrayEncodeMethods(
final StringBuilder sb,
final String propertyName,
final int sizeOfLengthField,
final int maxLengthValue,
final PrimitiveType lengthType,
final String byteOrderStr,
final String characterEncoding,
final String className,
final String indent)
{
final PrimitiveType lengthPutType = PrimitiveType.UINT32 == lengthType ? PrimitiveType.INT32 : lengthType;
if (isAsciiEncoding(characterEncoding))
{
new Formatter(sb).format("\n" +
indent + " public %1$s %2$s(final String value)\n" +
indent + " {\n" +
indent + " final int length = null == value ? 0 : value.length();\n" +
indent + " if (length > %3$d)\n" +
indent + " {\n" +
indent + " throw new IllegalStateException(\"length > maxValue for type: \" + length);\n" +
indent + " }\n\n" +
indent + " final int headerLength = %4$d;\n" +
indent + " final int limit = parentMessage.limit();\n" +
indent + " parentMessage.limit(limit + headerLength + length);\n" +
indent + " %5$s;\n" +
indent + " buffer.putStringWithoutLengthAscii(limit + headerLength, value);\n\n" +
indent + " return this;\n" +
indent + " }\n",
className,
formatPropertyName(propertyName),
maxLengthValue,
sizeOfLengthField,
generatePut(lengthPutType, "limit", "length", byteOrderStr));
new Formatter(sb).format("\n" +
indent + " public %1$s %2$s(final CharSequence value)\n" +
indent + " {\n" +
indent + " final int length = null == value ? 0 : value.length();\n" +
indent + " if (length > %3$d)\n" +
indent + " {\n" +
indent + " throw new IllegalStateException(\"length > maxValue for type: \" + length);\n" +
indent + " }\n\n" +
indent + " final int headerLength = %4$d;\n" +
indent + " final int limit = parentMessage.limit();\n" +
indent + " parentMessage.limit(limit + headerLength + length);\n" +
indent + " %5$s;\n" +
indent + " buffer.putStringWithoutLengthAscii(limit + headerLength, value);\n\n" +
indent + " return this;\n" +
indent + " }\n",
className,
formatPropertyName(propertyName),
maxLengthValue,
sizeOfLengthField,
generatePut(lengthPutType, "limit", "length", byteOrderStr));
}
else
{
new Formatter(sb).format("\n" +
indent + " public %1$s %2$s(final String value)\n" +
indent + " {\n" +
indent + " final byte[] bytes = (null == value || value.isEmpty()) ?" +
" org.agrona.collections.ArrayUtil.EMPTY_BYTE_ARRAY : value.getBytes(%3$s);\n\n" +
indent + " final int length = bytes.length;\n" +
indent + " if (length > %4$d)\n" +
indent + " {\n" +
indent + " throw new IllegalStateException(\"length > maxValue for type: \" + length);\n" +
indent + " }\n\n" +
indent + " final int headerLength = %5$d;\n" +
indent + " final int limit = parentMessage.limit();\n" +
indent + " parentMessage.limit(limit + headerLength + length);\n" +
indent + " %6$s;\n" +
indent + " buffer.putBytes(limit + headerLength, bytes, 0, length);\n\n" +
indent + " return this;\n" +
indent + " }\n",
className,
formatPropertyName(propertyName),
charset(characterEncoding),
maxLengthValue,
sizeOfLengthField,
generatePut(lengthPutType, "limit", "length", byteOrderStr));
}
}
private void generateVarDataTypedDecoder(
final StringBuilder sb,
final Token token,
final String propertyName,
final int sizeOfLengthField,
final String exchangeType,
final PrimitiveType lengthType,
final String byteOrderStr,
final String indent)
{
new Formatter(sb).format("\n" +
indent + " public int get%s(final %s dst, final int dstOffset, final int length)\n" +
indent + " {\n" +
"%s" +
indent + " final int headerLength = %d;\n" +
indent + " final int limit = parentMessage.limit();\n" +
indent + " final int dataLength = %s%s;\n" +
indent + " final int bytesCopied = Math.min(length, dataLength);\n" +
indent + " parentMessage.limit(limit + headerLength + dataLength);\n" +
indent + " buffer.getBytes(limit + headerLength, dst, dstOffset, bytesCopied);\n\n" +
indent + " return bytesCopied;\n" +
indent + " }\n",
propertyName,
exchangeType,
generateArrayFieldNotPresentCondition(token.version(), indent),
sizeOfLengthField,
PrimitiveType.UINT32 == lengthType ? "(int)" : "",
generateGet(lengthType, "limit", byteOrderStr));
}
private void generateDataTypedEncoder(
final StringBuilder sb,
final String className,
final String propertyName,
final int sizeOfLengthField,
final int maxLengthValue,
final String exchangeType,
final PrimitiveType lengthType,
final String byteOrderStr,
final String indent)
{
final PrimitiveType lengthPutType = PrimitiveType.UINT32 == lengthType ? PrimitiveType.INT32 : lengthType;
new Formatter(sb).format("\n" +
indent + " public %1$s put%2$s(final %3$s src, final int srcOffset, final int length)\n" +
indent + " {\n" +
indent + " if (length > %4$d)\n" +
indent + " {\n" +
indent + " throw new IllegalStateException(\"length > maxValue for type: \" + length);\n" +
indent + " }\n\n" +
indent + " final int headerLength = %5$d;\n" +
indent + " final int limit = parentMessage.limit();\n" +
indent + " parentMessage.limit(limit + headerLength + length);\n" +
indent + " %6$s;\n" +
indent + " buffer.putBytes(limit + headerLength, src, srcOffset, length);\n\n" +
indent + " return this;\n" +
indent + " }\n",
className,
propertyName,
exchangeType,
maxLengthValue,
sizeOfLengthField,
generatePut(lengthPutType, "limit", "length", byteOrderStr));
}
private void generateBitSet(final List tokens) throws IOException
{
final Token token = tokens.get(0);
final String bitSetName = token.applicableTypeName();
final String decoderName = decoderName(bitSetName);
final String encoderName = encoderName(bitSetName);
final List choiceList = tokens.subList(1, tokens.size() - 1);
final String implementsString = implementsInterface(Flyweight.class.getSimpleName());
registerTypesPackageName(token, ir);
try (Writer out = outputManager.createOutput(decoderName))
{
final Encoding encoding = token.encoding();
generateFixedFlyweightHeader(
out, token, decoderName, implementsString, readOnlyBuffer, fqReadOnlyBuffer, PACKAGES_EMPTY_SET);
out.append(generateChoiceIsEmpty(encoding.primitiveType()));
new Formatter(out).format(
"\n" +
" public %s getRaw()\n" +
" {\n" +
" return %s;\n" +
" }\n",
primitiveTypeName(token),
generateGet(encoding.primitiveType(), "offset", byteOrderString(encoding)));
generateChoiceDecoders(out, choiceList);
out.append(generateChoiceDisplay(choiceList));
out.append("}\n");
}
registerTypesPackageName(token, ir);
try (Writer out = outputManager.createOutput(encoderName))
{
generateFixedFlyweightHeader(
out, token, encoderName, implementsString, mutableBuffer, fqMutableBuffer, PACKAGES_EMPTY_SET);
generateChoiceClear(out, encoderName, token);
generateChoiceEncoders(out, encoderName, choiceList);
out.append("}\n");
}
}
private void generateFixedFlyweightHeader(
final Writer out,
final Token token,
final String typeName,
final String implementsString,
final String buffer,
final String fqBuffer,
final Set importedTypesPackages) throws IOException
{
out.append(generateFileHeader(registerTypesPackageName(token, ir), importedTypesPackages, fqBuffer));
out.append(generateDeclaration(typeName, implementsString, token));
out.append(generateFixedFlyweightCode(typeName, token.encodedLength(), buffer));
}
private void generateCompositeFlyweightHeader(
final Token token,
final String typeName,
final Writer out,
final String buffer,
final String fqBuffer,
final String implementsString,
final Set importedTypesPackages) throws IOException
{
out.append(generateFileHeader(registerTypesPackageName(token, ir), importedTypesPackages, fqBuffer));
out.append(generateDeclaration(typeName, implementsString, token));
out.append(generateFixedFlyweightCode(typeName, token.encodedLength(), buffer));
}
private void generateEnum(final List tokens) throws IOException
{
final Token enumToken = tokens.get(0);
final String enumName = formatClassName(enumToken.applicableTypeName());
final Encoding encoding = enumToken.encoding();
final String nullVal = encoding.applicableNullValue().toString();
final String packageName = registerTypesPackageName(enumToken, ir);
try (Writer out = outputManager.createOutput(enumName))
{
out.append(generateEnumFileHeader(packageName));
out.append(generateEnumDeclaration(enumName, enumToken));
final List valuesList = tokens.subList(1, tokens.size() - 1);
out.append(generateEnumValues(valuesList, generateLiteral(encoding.primitiveType(), nullVal)));
out.append(generateEnumBody(enumToken, enumName));
out.append(generateEnumLookupMethod(valuesList, enumName, nullVal));
out.append("}\n");
}
}
private void generateComposite(final List tokens) throws IOException
{
final Token token = tokens.get(0);
final String compositeName = token.applicableTypeName();
final String decoderName = decoderName(compositeName);
final String encoderName = encoderName(compositeName);
registerTypesPackageName(token, ir);
final Set importedTypesPackages = scanPackagesToImport(tokens);
try (Writer out = outputManager.createOutput(decoderName))
{
final String implementsString = implementsInterface(CompositeDecoderFlyweight.class.getSimpleName());
generateCompositeFlyweightHeader(
token, decoderName, out, readOnlyBuffer, fqReadOnlyBuffer, implementsString, importedTypesPackages);
for (int i = 1, end = tokens.size() - 1; i < end;)
{
final Token encodingToken = tokens.get(i);
final String propertyName = formatPropertyName(encodingToken.name());
final String typeName = decoderName(encodingToken.applicableTypeName());
final StringBuilder sb = new StringBuilder();
generateEncodingOffsetMethod(sb, propertyName, encodingToken.offset(), BASE_INDENT);
generateEncodingLengthMethod(sb, propertyName, encodingToken.encodedLength(), BASE_INDENT);
generateFieldSinceVersionMethod(sb, encodingToken, BASE_INDENT);
switch (encodingToken.signal())
{
case ENCODING:
generatePrimitiveDecoder(
sb, true, encodingToken.name(), encodingToken, encodingToken, BASE_INDENT);
break;
case BEGIN_ENUM:
generateEnumDecoder(sb, true, encodingToken, propertyName, encodingToken, BASE_INDENT);
break;
case BEGIN_SET:
generateBitSetProperty(
sb, true, DECODER, propertyName, encodingToken, encodingToken, BASE_INDENT, typeName);
break;
case BEGIN_COMPOSITE:
generateCompositeProperty(
sb, true, DECODER, propertyName, encodingToken, encodingToken, BASE_INDENT, typeName);
break;
default:
break;
}
out.append(sb);
i += encodingToken.componentTokenCount();
}
out.append(generateCompositeDecoderDisplay(tokens));
out.append("}\n");
}
registerTypesPackageName(token, ir);
try (Writer out = outputManager.createOutput(encoderName))
{
final String implementsString = implementsInterface(CompositeEncoderFlyweight.class.getSimpleName());
generateCompositeFlyweightHeader(
token, encoderName, out, mutableBuffer, fqMutableBuffer, implementsString, importedTypesPackages);
for (int i = 1, end = tokens.size() - 1; i < end;)
{
final Token encodingToken = tokens.get(i);
final String propertyName = formatPropertyName(encodingToken.name());
final String typeName = encoderName(encodingToken.applicableTypeName());
final StringBuilder sb = new StringBuilder();
generateEncodingOffsetMethod(sb, propertyName, encodingToken.offset(), BASE_INDENT);
generateEncodingLengthMethod(sb, propertyName, encodingToken.encodedLength(), BASE_INDENT);
switch (encodingToken.signal())
{
case ENCODING:
generatePrimitiveEncoder(sb, encoderName, encodingToken.name(), encodingToken, BASE_INDENT);
break;
case BEGIN_ENUM:
generateEnumEncoder(sb, encoderName, encodingToken, propertyName, encodingToken, BASE_INDENT);
break;
case BEGIN_SET:
generateBitSetProperty(
sb, true, ENCODER, propertyName, encodingToken, encodingToken, BASE_INDENT, typeName);
break;
case BEGIN_COMPOSITE:
generateCompositeProperty(
sb, true, ENCODER, propertyName, encodingToken, encodingToken, BASE_INDENT, typeName);
break;
default:
break;
}
out.append(sb);
i += encodingToken.componentTokenCount();
}
out.append(generateCompositeEncoderDisplay(decoderName));
out.append("}\n");
}
}
private Set scanPackagesToImport(final List tokens)
{
if (!shouldSupportTypesPackageNames)
{
return PACKAGES_EMPTY_SET;
}
final Set packagesToImport = new HashSet<>();
for (int i = 1, limit = tokens.size() - 1; i < limit; i++)
{
final Token typeToken = tokens.get(i);
if (typeToken.signal() == Signal.BEGIN_ENUM ||
typeToken.signal() == Signal.BEGIN_SET ||
typeToken.signal() == Signal.BEGIN_COMPOSITE)
{
if (typeToken.packageName() != null)
{
packagesToImport.add(typeToken.packageName());
}
}
}
return packagesToImport;
}
private void generateChoiceClear(final Appendable out, final String bitSetClassName, final Token token)
throws IOException
{
final Encoding encoding = token.encoding();
final String literalValue = generateLiteral(encoding.primitiveType(), "0");
final String byteOrderStr = byteOrderString(encoding);
final String clearStr = generatePut(encoding.primitiveType(), "offset", literalValue, byteOrderStr);
out.append("\n")
.append(" public ").append(bitSetClassName).append(" clear()\n")
.append(" {\n")
.append(" ").append(clearStr).append(";\n")
.append(" return this;\n")
.append(" }\n");
}
private void generateChoiceDecoders(final Appendable out, final List tokens)
throws IOException
{
for (final Token token : tokens)
{
if (token.signal() == Signal.CHOICE)
{
final String choiceName = formatPropertyName(token.name());
final Encoding encoding = token.encoding();
final String choiceBitIndex = encoding.constValue().toString();
final String byteOrderStr = byteOrderString(encoding);
final PrimitiveType primitiveType = encoding.primitiveType();
final String argType = bitsetArgType(primitiveType);
generateOptionDecodeJavadoc(out, INDENT, token);
final String choiceGet = generateChoiceGet(primitiveType, choiceBitIndex, byteOrderStr);
final String staticChoiceGet = generateStaticChoiceGet(primitiveType, choiceBitIndex);
out.append("\n")
.append(" public boolean ").append(choiceName).append("()\n")
.append(" {\n")
.append(" return ").append(choiceGet).append(";\n")
.append(" }\n\n")
.append(" public static boolean ").append(choiceName)
.append("(final ").append(argType).append(" value)\n")
.append(" {\n").append(" return ").append(staticChoiceGet).append(";\n")
.append(" }\n");
}
}
}
private void generateChoiceEncoders(final Appendable out, final String bitSetClassName, final List tokens)
throws IOException
{
for (final Token token : tokens)
{
if (token.signal() == Signal.CHOICE)
{
final String choiceName = formatPropertyName(token.name());
final Encoding encoding = token.encoding();
final String choiceBitIndex = encoding.constValue().toString();
final String byteOrderStr = byteOrderString(encoding);
final PrimitiveType primitiveType = encoding.primitiveType();
final String argType = bitsetArgType(primitiveType);
generateOptionEncodeJavadoc(out, INDENT, token);
final String choicePut = generateChoicePut(encoding.primitiveType(), choiceBitIndex, byteOrderStr);
final String staticChoicePut = generateStaticChoicePut(encoding.primitiveType(), choiceBitIndex);
out.append("\n")
.append(" public ").append(bitSetClassName).append(" ").append(choiceName)
.append("(final boolean value)\n")
.append(" {\n")
.append(choicePut).append("\n")
.append(" return this;\n")
.append(" }\n\n")
.append(" public static ").append(argType).append(" ").append(choiceName)
.append("(final ").append(argType).append(" bits, final boolean value)\n")
.append(" {\n")
.append(staticChoicePut)
.append(" }\n");
}
}
}
private String bitsetArgType(final PrimitiveType primitiveType)
{
switch (primitiveType)
{
case UINT8:
return "byte";
case UINT16:
return "short";
case UINT32:
return "int";
case UINT64:
return "long";
default:
throw new IllegalStateException("Invalid type: " + primitiveType);
}
}
private CharSequence generateEnumValues(final List tokens, final String nullVal)
{
final StringBuilder sb = new StringBuilder();
for (final Token token : tokens)
{
final Encoding encoding = token.encoding();
final CharSequence constVal = generateLiteral(encoding.primitiveType(), encoding.constValue().toString());
generateTypeJavadoc(sb, INDENT, token);
sb.append(INDENT).append(token.name()).append('(').append(constVal).append("),\n\n");
}
if (shouldDecodeUnknownEnumValues)
{
sb.append(INDENT).append("/**\n");
sb.append(INDENT).append(" * To be used to represent an unknown value from a later version.\n");
sb.append(INDENT).append(" */\n");
sb.append(INDENT).append("SBE_UNKNOWN").append('(').append(nullVal).append("),\n\n");
}
sb.append(INDENT).append("/**\n");
sb.append(INDENT).append(" * To be used to represent not present or null.\n");
sb.append(INDENT).append(" */\n");
sb.append(INDENT).append("NULL_VAL").append('(').append(nullVal).append(");\n\n");
return sb;
}
private CharSequence generateEnumBody(final Token token, final String enumName)
{
final String javaEncodingType = primitiveTypeName(token);
return
" private final " + javaEncodingType + " value;\n\n" +
" " + enumName + "(final " + javaEncodingType + " value)\n" +
" {\n" +
" this.value = value;\n" +
" }\n\n" +
" /**\n" +
" * The raw encoded value in the Java type representation.\n" +
" *\n" +
" * @return the raw value encoded.\n" +
" */\n" +
" public " + javaEncodingType + " value()\n" +
" {\n" +
" return value;\n" +
" }\n";
}
private CharSequence generateEnumLookupMethod(final List tokens, final String enumName, final String nullVal)
{
final StringBuilder sb = new StringBuilder();
final PrimitiveType primitiveType = tokens.get(0).encoding().primitiveType();
sb.append("\n")
.append(" /**\n")
.append(" * Lookup the enum value representing the value.\n")
.append(" *\n")
.append(" * @param value encoded to be looked up.\n")
.append(" * @return the enum value representing the value.\n")
.append(" */\n")
.append(" public static ").append(enumName)
.append(" get(final ").append(javaTypeName(primitiveType)).append(" value)\n").append(" {\n")
.append(" switch (value)\n").append(" {\n");
for (final Token token : tokens)
{
final String constStr = token.encoding().constValue().toString();
final String name = token.name();
sb.append(" case ").append(constStr).append(": return ").append(name).append(";\n");
}
sb.append(" case ").append(nullVal).append(": return NULL_VAL").append(";\n");
final String handleUnknownLogic = shouldDecodeUnknownEnumValues ?
INDENT + INDENT + "return SBE_UNKNOWN;\n" :
INDENT + INDENT + "throw new IllegalArgumentException(\"Unknown value: \" + value);\n";
sb.append(" }\n\n")
.append(handleUnknownLogic)
.append(" }\n");
return sb;
}
private StringBuilder generateImportStatements(final Set packages, final String currentPackage)
{
final StringBuilder importStatements = new StringBuilder();
for (final String candidatePackage : packages)
{
if (!candidatePackage.equals(currentPackage))
{
importStatements.append("import ").append(candidatePackage).append(".*;\n");
}
}
if (importStatements.length() > 0)
{
importStatements.append("\n\n");
}
return importStatements;
}
private String interfaceImportLine()
{
if (!shouldGenerateInterfaces)
{
return "\n";
}
return "import " + JAVA_INTERFACE_PACKAGE + ".*;\n\n";
}
private CharSequence generateFileHeader(final String packageName, final Set importedTypesPackages,
final String fqBuffer)
{
final StringBuilder importStatements = generateImportStatements(importedTypesPackages, packageName);
return "/* Generated SBE (Simple Binary Encoding) message codec. */\n" +
"package " + packageName + ";\n\n" +
"import " + fqBuffer + ";\n" +
interfaceImportLine() +
importStatements;
}
private CharSequence generateMainHeader(
final String packageName, final CodecType codecType, final boolean hasVarData)
{
final StringBuilder importStatements = generateImportStatements(packageNameByTypes, packageName);
if (fqMutableBuffer.equals(fqReadOnlyBuffer))
{
return
"/* Generated SBE (Simple Binary Encoding) message codec. */\n" +
"package " + packageName + ";\n\n" +
"import " + fqMutableBuffer + ";\n" +
interfaceImportLine() +
importStatements;
}
else
{
final boolean hasMutableBuffer = ENCODER == codecType || hasVarData;
final boolean hasReadOnlyBuffer = DECODER == codecType || hasVarData;
return
"/* Generated SBE (Simple Binary Encoding) message codec. */\n" +
"package " + packageName + ";\n\n" +
(hasMutableBuffer ? "import " + fqMutableBuffer + ";\n" : "") +
(hasReadOnlyBuffer ? "import " + fqReadOnlyBuffer + ";\n" : "") +
interfaceImportLine() +
importStatements;
}
}
private static CharSequence generateEnumFileHeader(final String packageName)
{
return
"/* Generated SBE (Simple Binary Encoding) message codec. */\n" +
"package " + packageName + ";\n\n";
}
private void generateAnnotations(
final String indent,
final String className,
final List tokens,
final Appendable out,
final Function nameMapping) throws IOException
{
final List groupClassNames = new ArrayList<>();
int level = 0;
for (final Token token : tokens)
{
if (token.signal() == Signal.BEGIN_GROUP)
{
if (1 == ++level)
{
groupClassNames.add(formatClassName(nameMapping.apply(token.name())));
}
}
else if (token.signal() == Signal.END_GROUP)
{
--level;
}
}
if (!groupClassNames.isEmpty())
{
out.append(indent).append("@uk.co.real_logic.sbe.codec.java.GroupOrder({\n");
int i = 0;
for (final String name : groupClassNames)
{
out.append(indent).append(INDENT).append(className).append('.').append(name).append(".class");
if (++i < groupClassNames.size())
{
out.append(",\n");
}
}
out.append("})");
}
}
private static CharSequence generateDeclaration(
final String className, final String implementsString, final Token typeToken)
{
final StringBuilder sb = new StringBuilder();
generateTypeJavadoc(sb, BASE_INDENT, typeToken);
if (typeToken.deprecated() > 0)
{
sb.append("@Deprecated\n");
}
sb.append("@SuppressWarnings(\"all\")\n")
.append("public final class ").append(className).append(implementsString).append('\n')
.append("{\n");
return sb;
}
private void generatePackageInfo() throws IOException
{
try (Writer out = outputManager.createOutput(PACKAGE_INFO))
{
out.append(
"/* Generated SBE (Simple Binary Encoding) message codecs.*/\n" +
"/**\n" +
" * ").append(ir.description()).append("\n")
.append(
" */\n" +
"package ").append(ir.applicableNamespace()).append(";\n");
}
}
private void generateMetaAttributeEnum() throws IOException
{
try (Writer out = outputManager.createOutput(META_ATTRIBUTE_ENUM))
{
out.append(
"/* Generated SBE (Simple Binary Encoding) message codec. */\n" +
"package ").append(ir.applicableNamespace()).append(";\n\n")
.append(
"/**\n" +
" * Meta attribute enum for selecting a particular meta attribute value.\n" +
" */\n" +
" @SuppressWarnings(\"all\")\n" +
"public enum MetaAttribute\n" +
"{\n" +
" /**\n" +
" * The epoch or start of time. Default is 'UNIX' which is midnight 1st January 1970 UTC.\n" +
" */\n" +
" EPOCH,\n\n" +
" /**\n" +
" * Time unit applied to the epoch. Can be second, millisecond, microsecond, or nanosecond.\n" +
" */\n" +
" TIME_UNIT,\n\n" +
" /**\n" +
" * The type relationship to a FIX tag value encoded type. For reference only.\n" +
" */\n" +
" SEMANTIC_TYPE,\n\n" +
" /**\n" +
" * Field presence indication. Can be optional, required, or constant.\n" +
" */\n" +
" PRESENCE\n" +
"}\n");
}
}
private static CharSequence generateEnumDeclaration(final String name, final Token typeToken)
{
final StringBuilder sb = new StringBuilder();
generateTypeJavadoc(sb, BASE_INDENT, typeToken);
sb.append("@SuppressWarnings(\"all\")\n").append("public enum ").append(name).append("\n{\n");
return sb;
}
private void generatePrimitiveDecoder(
final StringBuilder sb,
final boolean inComposite,
final String propertyName,
final Token propertyToken,
final Token encodingToken,
final String indent)
{
final String formattedPropertyName = formatPropertyName(propertyName);
generatePrimitiveFieldMetaMethod(sb, formattedPropertyName, encodingToken, indent);
if (encodingToken.isConstantEncoding())
{
generateConstPropertyMethods(sb, formattedPropertyName, encodingToken, indent);
}
else
{
sb.append(generatePrimitivePropertyDecodeMethods(
inComposite, formattedPropertyName, propertyToken, encodingToken, indent));
}
}
private void generatePrimitiveEncoder(
final StringBuilder sb,
final String containingClassName,
final String propertyName,
final Token token,
final String indent)
{
final String formattedPropertyName = formatPropertyName(propertyName);
generatePrimitiveFieldMetaMethod(sb, formattedPropertyName, token, indent);
if (!token.isConstantEncoding())
{
sb.append(generatePrimitivePropertyEncodeMethods(
containingClassName, formattedPropertyName, token, indent));
}
else
{
generateConstPropertyMethods(sb, formattedPropertyName, token, indent);
}
}
private CharSequence generatePrimitivePropertyDecodeMethods(
final boolean inComposite,
final String propertyName,
final Token propertyToken,
final Token encodingToken,
final String indent)
{
return encodingToken.matchOnLength(
() -> generatePrimitivePropertyDecode(inComposite, propertyName, propertyToken, encodingToken, indent),
() -> generatePrimitiveArrayPropertyDecode(
inComposite, propertyName, propertyToken, encodingToken, indent));
}
private CharSequence generatePrimitivePropertyEncodeMethods(
final String containingClassName, final String propertyName, final Token token, final String indent)
{
return token.matchOnLength(
() -> generatePrimitivePropertyEncode(containingClassName, propertyName, token, indent),
() -> generatePrimitiveArrayPropertyEncode(containingClassName, propertyName, token, indent));
}
private void generatePrimitiveFieldMetaMethod(
final StringBuilder sb, final String propertyName, final Token token, final String indent)
{
final PrimitiveType primitiveType = token.encoding().primitiveType();
final String javaTypeName = javaTypeName(primitiveType);
final String formattedPropertyName = formatPropertyName(propertyName);
final String nullValue = generateLiteral(primitiveType, token.encoding().applicableNullValue().toString());
generatePrimitiveFieldMetaMethod(sb, indent, javaTypeName, formattedPropertyName, "Null", nullValue);
final String minValue = generateLiteral(primitiveType, token.encoding().applicableMinValue().toString());
generatePrimitiveFieldMetaMethod(sb, indent, javaTypeName, formattedPropertyName, "Min", minValue);
final String maxValue = generateLiteral(primitiveType, token.encoding().applicableMaxValue().toString());
generatePrimitiveFieldMetaMethod(sb, indent, javaTypeName, formattedPropertyName, "Max", maxValue);
}
private void generatePrimitiveFieldMetaMethod(
final StringBuilder sb,
final String indent,
final String javaTypeName,
final String formattedPropertyName,
final String metaType,
final String retValue)
{
sb.append("\n")
.append(indent).append(" public static ")
.append(javaTypeName).append(" ").append(formattedPropertyName).append(metaType).append("Value()\n")
.append(indent).append(" {\n")
.append(indent).append(" return ").append(retValue).append(";\n")
.append(indent).append(" }\n");
}
private CharSequence generatePrimitivePropertyDecode(
final boolean inComposite,
final String propertyName,
final Token propertyToken,
final Token encodingToken,
final String indent)
{
final Encoding encoding = encodingToken.encoding();
final String javaTypeName = javaTypeName(encoding.primitiveType());
final int offset = encodingToken.offset();
final String byteOrderStr = byteOrderString(encoding);
return String.format(
"\n" +
indent + " public %s %s()\n" +
indent + " {\n" +
"%s" +
indent + " return %s;\n" +
indent + " }\n\n",
javaTypeName,
formatPropertyName(propertyName),
generateFieldNotPresentCondition(inComposite, propertyToken.version(), encoding, indent),
generateGet(encoding.primitiveType(), "offset + " + offset, byteOrderStr));
}
private CharSequence generatePrimitivePropertyEncode(
final String containingClassName, final String propertyName, final Token token, final String indent)
{
final Encoding encoding = token.encoding();
final String javaTypeName = javaTypeName(encoding.primitiveType());
final int offset = token.offset();
final String byteOrderStr = byteOrderString(encoding);
return String.format(
"\n" +
indent + " public %s %s(final %s value)\n" +
indent + " {\n" +
indent + " %s;\n" +
indent + " return this;\n" +
indent + " }\n\n",
formatClassName(containingClassName),
formatPropertyName(propertyName),
javaTypeName,
generatePut(encoding.primitiveType(), "offset + " + offset, "value", byteOrderStr));
}
private CharSequence generateWrapFieldNotPresentCondition(final int sinceVersion, final String indent)
{
if (0 == sinceVersion)
{
return "";
}
return
indent + " if (parentMessage.actingVersion < " + sinceVersion + ")\n" +
indent + " {\n" +
indent + " wrapBuffer.wrap(buffer, offset, 0);\n" +
indent + " return;\n" +
indent + " }\n\n";
}
private CharSequence generateFieldNotPresentCondition(
final boolean inComposite, final int sinceVersion, final Encoding encoding, final String indent)
{
if (inComposite || 0 == sinceVersion)
{
return "";
}
final String nullValue = generateLiteral(encoding.primitiveType(), encoding.applicableNullValue().toString());
return
indent + " if (parentMessage.actingVersion < " + sinceVersion + ")\n" +
indent + " {\n" +
indent + " return " + nullValue + ";\n" +
indent + " }\n\n";
}
private static CharSequence generateArrayFieldNotPresentCondition(final int sinceVersion, final String indent)
{
if (0 == sinceVersion)
{
return "";
}
return
indent + " if (parentMessage.actingVersion < " + sinceVersion + ")\n" +
indent + " {\n" +
indent + " return 0;\n" +
indent + " }\n\n";
}
private static CharSequence generateStringNotPresentConditionForAppendable(
final int sinceVersion, final String indent)
{
if (0 == sinceVersion)
{
return "";
}
return
indent + " if (parentMessage.actingVersion < " + sinceVersion + ")\n" +
indent + " {\n" +
indent + " return 0;\n" +
indent + " }\n\n";
}
private static CharSequence generateStringNotPresentCondition(final int sinceVersion, final String indent)
{
if (0 == sinceVersion)
{
return "";
}
return
indent + " if (parentMessage.actingVersion < " + sinceVersion + ")\n" +
indent + " {\n" +
indent + " return \"\";\n" +
indent + " }\n\n";
}
private static CharSequence generatePropertyNotPresentCondition(
final boolean inComposite,
final CodecType codecType,
final Token propertyToken,
final String enumName,
final String indent)
{
if (inComposite || codecType == ENCODER || 0 == propertyToken.version())
{
return "";
}
final String nullValue = enumName == null ? "null" : (enumName + ".NULL_VAL");
return
indent + " if (parentMessage.actingVersion < " + propertyToken.version() + ")\n" +
indent + " {\n" +
indent + " return " + nullValue + ";\n" +
indent + " }\n\n";
}
private CharSequence generatePrimitiveArrayPropertyDecode(
final boolean inComposite,
final String propertyName,
final Token propertyToken,
final Token encodingToken,
final String indent)
{
final Encoding encoding = encodingToken.encoding();
final String javaTypeName = javaTypeName(encoding.primitiveType());
final int offset = encodingToken.offset();
final String byteOrderStr = byteOrderString(encoding);
final int fieldLength = encodingToken.arrayLength();
final int typeSize = sizeOfPrimitive(encoding);
final StringBuilder sb = new StringBuilder();
generateArrayLengthMethod(propertyName, indent, fieldLength, sb);
new Formatter(sb).format("\n" +
indent + " public %s %s(final int index)\n" +
indent + " {\n" +
indent + " if (index < 0 || index >= %d)\n" +
indent + " {\n" +
indent + " throw new IndexOutOfBoundsException(\"index out of range: index=\" + index);\n" +
indent + " }\n\n" +
"%s" +
indent + " final int pos = offset + %d + (index * %d);\n\n" +
indent + " return %s;\n" +
indent + " }\n\n",
javaTypeName,
propertyName,
fieldLength,
generateFieldNotPresentCondition(inComposite, propertyToken.version(), encoding, indent),
offset,
typeSize,
generateGet(encoding.primitiveType(), "pos", byteOrderStr));
if (encoding.primitiveType() == PrimitiveType.CHAR)
{
generateCharacterEncodingMethod(sb, propertyName, encoding.characterEncoding(), indent);
new Formatter(sb).format("\n" +
indent + " public int get%s(final byte[] dst, final int dstOffset)\n" +
indent + " {\n" +
indent + " final int length = %d;\n" +
indent + " if (dstOffset < 0 || dstOffset > (dst.length - length))\n" +
indent + " {\n" +
indent + " throw new IndexOutOfBoundsException(" +
"\"Copy will go out of range: offset=\" + dstOffset);\n" +
indent + " }\n\n" +
"%s" +
indent + " buffer.getBytes(offset + %d, dst, dstOffset, length);\n\n" +
indent + " return length;\n" +
indent + " }\n",
Generators.toUpperFirstChar(propertyName),
fieldLength,
generateArrayFieldNotPresentCondition(propertyToken.version(), indent),
offset);
new Formatter(sb).format("\n" +
indent + " public String %s()\n" +
indent + " {\n" +
"%s" +
indent + " final byte[] dst = new byte[%d];\n" +
indent + " buffer.getBytes(offset + %d, dst, 0, %d);\n\n" +
indent + " int end = 0;\n" +
indent + " for (; end < %d && dst[end] != 0; ++end);\n\n" +
indent + " return new String(dst, 0, end, %s);\n" +
indent + " }\n\n",
propertyName,
generateStringNotPresentCondition(propertyToken.version(), indent),
fieldLength,
offset,
fieldLength,
fieldLength,
charset(encoding.characterEncoding()));
if (isAsciiEncoding(encoding.characterEncoding()))
{
new Formatter(sb).format("\n" +
indent + " public int get%1$s(final Appendable value)\n" +
indent + " {\n" +
"%2$s" +
indent + " for (int i = 0; i < %3$d; ++i)\n" +
indent + " {\n" +
indent + " final int c = buffer.getByte(offset + %4$d + i) & 0xFF;\n" +
indent + " if (c == 0)\n" +
indent + " {\n" +
indent + " return i;\n" +
indent + " }\n\n" +
indent + " try\n" +
indent + " {\n" +
indent + " value.append(c > 127 ? '?' : (char)c);\n" +
indent + " }\n" +
indent + " catch (final java.io.IOException ex)\n" +
indent + " {\n" +
indent + " throw new java.io.UncheckedIOException(ex);\n" +
indent + " }\n" +
indent + " }\n\n" +
indent + " return %3$d;\n" +
indent + " }\n\n",
Generators.toUpperFirstChar(propertyName),
generateStringNotPresentConditionForAppendable(propertyToken.version(), indent),
fieldLength,
offset);
}
}
else if (encoding.primitiveType() == PrimitiveType.UINT8)
{
new Formatter(sb).format("\n" +
indent + " public int get%s(final byte[] dst, final int dstOffset, final int length)\n" +
indent + " {\n" +
"%s" +
indent + " final int bytesCopied = Math.min(length, %d);\n" +
indent + " buffer.getBytes(offset + %d, dst, dstOffset, bytesCopied);\n\n" +
indent + " return bytesCopied;\n" +
indent + " }\n",
Generators.toUpperFirstChar(propertyName),
generateArrayFieldNotPresentCondition(propertyToken.version(), indent),
fieldLength,
offset);
new Formatter(sb).format("\n" +
indent + " public int get%s(final %s dst, final int dstOffset, final int length)\n" +
indent + " {\n" +
"%s" +
indent + " final int bytesCopied = Math.min(length, %d);\n" +
indent + " buffer.getBytes(offset + %d, dst, dstOffset, bytesCopied);\n\n" +
indent + " return bytesCopied;\n" +
indent + " }\n",
Generators.toUpperFirstChar(propertyName),
fqMutableBuffer,
generateArrayFieldNotPresentCondition(propertyToken.version(), indent),
fieldLength,
offset);
new Formatter(sb).format("\n" +
indent + " public void wrap%s(final %s wrapBuffer)\n" +
indent + " {\n" +
"%s" +
indent + " wrapBuffer.wrap(buffer, offset + %d, %d);\n" +
indent + " }\n",
Generators.toUpperFirstChar(propertyName),
readOnlyBuffer,
generateWrapFieldNotPresentCondition(propertyToken.version(), indent),
offset,
fieldLength);
}
return sb;
}
private static void generateArrayLengthMethod(
final String propertyName, final String indent, final int fieldLength, final StringBuilder sb)
{
final String formatPropertyName = formatPropertyName(propertyName);
sb.append("\n")
.append(indent).append(" public static int ").append(formatPropertyName).append("Length()\n")
.append(indent).append(" {\n")
.append(indent).append(" return ").append(fieldLength).append(";\n")
.append(indent).append(" }\n\n");
}
private String byteOrderString(final Encoding encoding)
{
return sizeOfPrimitive(encoding) == 1 ? "" : ", java.nio.ByteOrder." + encoding.byteOrder();
}
private CharSequence generatePrimitiveArrayPropertyEncode(
final String containingClassName, final String propertyName, final Token token, final String indent)
{
final Encoding encoding = token.encoding();
final PrimitiveType primitiveType = encoding.primitiveType();
final String javaTypeName = javaTypeName(primitiveType);
final int offset = token.offset();
final String byteOrderStr = byteOrderString(encoding);
final int arrayLength = token.arrayLength();
final int typeSize = sizeOfPrimitive(encoding);
final StringBuilder sb = new StringBuilder();
final String className = formatClassName(containingClassName);
generateArrayLengthMethod(propertyName, indent, arrayLength, sb);
new Formatter(sb).format("\n" +
indent + " public %s %s(final int index, final %s value)\n" +
indent + " {\n" +
indent + " if (index < 0 || index >= %d)\n" +
indent + " {\n" +
indent + " throw new IndexOutOfBoundsException(\"index out of range: index=\" + index);\n" +
indent + " }\n\n" +
indent + " final int pos = offset + %d + (index * %d);\n" +
indent + " %s;\n\n" +
indent + " return this;\n" +
indent + " }\n",
className,
propertyName,
javaTypeName,
arrayLength,
offset,
typeSize,
generatePut(primitiveType, "pos", "value", byteOrderStr));
if (arrayLength > 1 && arrayLength <= 4)
{
sb.append(indent)
.append(" public ")
.append(className)
.append(" put").append(Generators.toUpperFirstChar(propertyName))
.append("(final ").append(javaTypeName).append(" value0");
for (int i = 1; i < arrayLength; i++)
{
sb.append(", final ").append(javaTypeName).append(" value").append(i);
}
sb.append(")\n");
sb.append(indent).append(" {\n");
for (int i = 0; i < arrayLength; i++)
{
final String indexStr = "offset + " + (offset + (typeSize * i));
sb.append(indent).append(" ")
.append(generatePut(primitiveType, indexStr, "value" + i, byteOrderStr))
.append(";\n");
}
sb.append("\n");
sb.append(indent).append(" return this;\n");
sb.append(indent).append(" }\n");
}
if (primitiveType == PrimitiveType.CHAR)
{
generateCharArrayEncodeMethods(
containingClassName, propertyName, indent, encoding, offset, arrayLength, sb);
}
else if (primitiveType == PrimitiveType.UINT8)
{
generateByteArrayEncodeMethods(
containingClassName, propertyName, indent, offset, arrayLength, sb);
}
return sb;
}
private void generateCharArrayEncodeMethods(
final String containingClassName,
final String propertyName,
final String indent,
final Encoding encoding,
final int offset,
final int fieldLength,
final StringBuilder sb)
{
generateCharacterEncodingMethod(sb, propertyName, encoding.characterEncoding(), indent);
new Formatter(sb).format("\n" +
indent + " public %s put%s(final byte[] src, final int srcOffset)\n" +
indent + " {\n" +
indent + " final int length = %d;\n" +
indent + " if (srcOffset < 0 || srcOffset > (src.length - length))\n" +
indent + " {\n" +
indent + " throw new IndexOutOfBoundsException(" +
"\"Copy will go out of range: offset=\" + srcOffset);\n" +
indent + " }\n\n" +
indent + " buffer.putBytes(offset + %d, src, srcOffset, length);\n\n" +
indent + " return this;\n" +
indent + " }\n",
formatClassName(containingClassName),
Generators.toUpperFirstChar(propertyName),
fieldLength,
offset);
if (isAsciiEncoding(encoding.characterEncoding()))
{
new Formatter(sb).format("\n" +
indent + " public %1$s %2$s(final String src)\n" +
indent + " {\n" +
indent + " final int length = %3$d;\n" +
indent + " final int srcLength = null == src ? 0 : src.length();\n" +
indent + " if (srcLength > length)\n" +
indent + " {\n" +
indent + " throw new IndexOutOfBoundsException(" +
"\"String too large for copy: byte length=\" + srcLength);\n" +
indent + " }\n\n" +
indent + " buffer.putStringWithoutLengthAscii(offset + %4$d, src);\n\n" +
indent + " for (int start = srcLength; start < length; ++start)\n" +
indent + " {\n" +
indent + " buffer.putByte(offset + %4$d + start, (byte)0);\n" +
indent + " }\n\n" +
indent + " return this;\n" +
indent + " }\n",
formatClassName(containingClassName),
propertyName,
fieldLength,
offset);
new Formatter(sb).format("\n" +
indent + " public %1$s %2$s(final CharSequence src)\n" +
indent + " {\n" +
indent + " final int length = %3$d;\n" +
indent + " final int srcLength = null == src ? 0 : src.length();\n" +
indent + " if (srcLength > length)\n" +
indent + " {\n" +
indent + " throw new IndexOutOfBoundsException(" +
"\"CharSequence too large for copy: byte length=\" + srcLength);\n" +
indent + " }\n\n" +
indent + " buffer.putStringWithoutLengthAscii(offset + %4$d, src);\n\n" +
indent + " for (int start = srcLength; start < length; ++start)\n" +
indent + " {\n" +
indent + " buffer.putByte(offset + %4$d + start, (byte)0);\n" +
indent + " }\n\n" +
indent + " return this;\n" +
indent + " }\n",
formatClassName(containingClassName),
propertyName,
fieldLength,
offset);
}
else
{
new Formatter(sb).format("\n" +
indent + " public %s %s(final String src)\n" +
indent + " {\n" +
indent + " final int length = %d;\n" +
indent + " final byte[] bytes = (null == src || src.isEmpty()) ?" +
" org.agrona.collections.ArrayUtil.EMPTY_BYTE_ARRAY : src.getBytes(%s);\n" +
indent + " if (bytes.length > length)\n" +
indent + " {\n" +
indent + " throw new IndexOutOfBoundsException(" +
"\"String too large for copy: byte length=\" + bytes.length);\n" +
indent + " }\n\n" +
indent + " buffer.putBytes(offset + %d, bytes, 0, bytes.length);\n\n" +
indent + " for (int start = bytes.length; start < length; ++start)\n" +
indent + " {\n" +
indent + " buffer.putByte(offset + %d + start, (byte)0);\n" +
indent + " }\n\n" +
indent + " return this;\n" +
indent + " }\n",
formatClassName(containingClassName),
propertyName,
fieldLength,
charset(encoding.characterEncoding()),
offset,
offset);
}
}
private void generateByteArrayEncodeMethods(
final String containingClassName,
final String propertyName,
final String indent,
final int offset,
final int fieldLength,
final StringBuilder sb)
{
new Formatter(sb).format("\n" +
indent + " public %s put%s(final byte[] src, final int srcOffset, final int length)\n" +
indent + " {\n" +
indent + " if (length > %d)\n" +
indent + " {\n" +
indent + " throw new IllegalStateException(" +
"\"length > maxValue for type: \" + length);\n" +
indent + " }\n\n" +
indent + " buffer.putBytes(offset + %d, src, srcOffset, length);\n" +
indent + " for (int i = length; i < %d; i++)\n" +
indent + " {\n" +
indent + " buffer.putByte(offset + %d + i, (byte)0);\n" +
indent + " }\n\n" +
indent + " return this;\n" +
indent + " }\n",
formatClassName(containingClassName),
Generators.toUpperFirstChar(propertyName),
fieldLength,
offset,
fieldLength,
offset);
new Formatter(sb).format("\n" +
indent + " public %s put%s(final %s src, final int srcOffset, final int length)\n" +
indent + " {\n" +
indent + " if (length > %d)\n" +
indent + " {\n" +
indent + " throw new IllegalStateException(" +
"\"length > maxValue for type: \" + length);\n" +
indent + " }\n\n" +
indent + " buffer.putBytes(offset + %d, src, srcOffset, length);\n" +
indent + " for (int i = length; i < %d; i++)\n" +
indent + " {\n" +
indent + " buffer.putByte(offset + %d + i, (byte)0);\n" +
indent + " }\n\n" +
indent + " return this;\n" +
indent + " }\n",
formatClassName(containingClassName),
Generators.toUpperFirstChar(propertyName),
fqReadOnlyBuffer,
fieldLength,
offset,
fieldLength,
offset);
}
private static int sizeOfPrimitive(final Encoding encoding)
{
return encoding.primitiveType().size();
}
private static void generateCharacterEncodingMethod(
final StringBuilder sb, final String propertyName, final String characterEncoding, final String indent)
{
if (null != characterEncoding)
{
final String propName = formatPropertyName(propertyName);
sb.append("\n")
.append(indent).append(" public static String ").append(propName).append("CharacterEncoding()\n")
.append(indent).append(" {\n")
.append(indent).append(" return ").append(charsetName(characterEncoding)).append(";\n")
.append(indent).append(" }\n");
}
}
private void generateConstPropertyMethods(
final StringBuilder sb, final String propertyName, final Token token, final String indent)
{
final String formattedPropertyName = formatPropertyName(propertyName);
final Encoding encoding = token.encoding();
if (encoding.primitiveType() != PrimitiveType.CHAR)
{
new Formatter(sb).format("\n" +
indent + " public %s %s()\n" +
indent + " {\n" +
indent + " return %s;\n" +
indent + " }\n",
javaTypeName(encoding.primitiveType()),
formattedPropertyName,
generateLiteral(encoding.primitiveType(), encoding.constValue().toString()));
return;
}
final String javaTypeName = javaTypeName(encoding.primitiveType());
final byte[] constBytes = encoding.constValue().byteArrayValue(encoding.primitiveType());
final CharSequence values = generateByteLiteralList(
encoding.constValue().byteArrayValue(encoding.primitiveType()));
new Formatter(sb).format("\n" +
"\n" +
indent + " private static final byte[] %s_VALUE = { %s };\n",
propertyName.toUpperCase(),
values);
generateArrayLengthMethod(formattedPropertyName, indent, constBytes.length, sb);
new Formatter(sb).format("\n" +
indent + " public %s %s(final int index)\n" +
indent + " {\n" +
indent + " return %s_VALUE[index];\n" +
indent + " }\n\n",
javaTypeName,
formattedPropertyName,
propertyName.toUpperCase());
sb.append(String.format(
indent + " public int get%s(final byte[] dst, final int offset, final int length)\n" +
indent + " {\n" +
indent + " final int bytesCopied = Math.min(length, %d);\n" +
indent + " System.arraycopy(%s_VALUE, 0, dst, offset, bytesCopied);\n\n" +
indent + " return bytesCopied;\n" +
indent + " }\n",
Generators.toUpperFirstChar(propertyName),
constBytes.length,
propertyName.toUpperCase()));
if (constBytes.length > 1)
{
new Formatter(sb).format("\n" +
indent + " public String %s()\n" +
indent + " {\n" +
indent + " return \"%s\";\n" +
indent + " }\n\n",
formattedPropertyName,
encoding.constValue());
}
else
{
new Formatter(sb).format("\n" +
indent + " public byte %s()\n" +
indent + " {\n" +
indent + " return (byte)%s;\n" +
indent + " }\n\n",
formattedPropertyName,
encoding.constValue());
}
}
private static CharSequence generateByteLiteralList(final byte[] bytes)
{
final StringBuilder values = new StringBuilder();
for (final byte b : bytes)
{
values.append(b).append(", ");
}
if (values.length() > 0)
{
values.setLength(values.length() - 2);
}
return values;
}
private CharSequence generateFixedFlyweightCode(
final String className, final int size, final String bufferImplementation)
{
final String schemaIdType = javaTypeName(ir.headerStructure().schemaIdType());
final String schemaIdAccessorType = shouldGenerateInterfaces ? "int" : schemaIdType;
final String schemaVersionType = javaTypeName(ir.headerStructure().schemaVersionType());
final String schemaVersionAccessorType = shouldGenerateInterfaces ? "int" : schemaVersionType;
final String semanticVersion = ir.semanticVersion() == null ? "" : ir.semanticVersion();
return String.format(
" public static final %5$s SCHEMA_ID = %6$s;\n" +
" public static final %7$s SCHEMA_VERSION = %8$s;\n" +
" public static final String SEMANTIC_VERSION = \"%11$s\";\n" +
" public static final int ENCODED_LENGTH = %2$d;\n" +
" public static final java.nio.ByteOrder BYTE_ORDER = java.nio.ByteOrder.%4$s;\n\n" +
" private int offset;\n" +
" private %3$s buffer;\n\n" +
" public %1$s wrap(final %3$s buffer, final int offset)\n" +
" {\n" +
" if (buffer != this.buffer)\n" +
" {\n" +
" this.buffer = buffer;\n" +
" }\n" +
" this.offset = offset;\n\n" +
" return this;\n" +
" }\n\n" +
" public %3$s buffer()\n" +
" {\n" +
" return buffer;\n" +
" }\n\n" +
" public int offset()\n" +
" {\n" +
" return offset;\n" +
" }\n\n" +
" public int encodedLength()\n" +
" {\n" +
" return ENCODED_LENGTH;\n" +
" }\n\n" +
" public %9$s sbeSchemaId()\n" +
" {\n" +
" return SCHEMA_ID;\n" +
" }\n\n" +
" public %10$s sbeSchemaVersion()\n" +
" {\n" +
" return SCHEMA_VERSION;\n" +
" }\n",
className,
size,
bufferImplementation,
ir.byteOrder(),
schemaIdType,
generateLiteral(ir.headerStructure().schemaIdType(), Integer.toString(ir.id())),
schemaVersionType,
generateLiteral(ir.headerStructure().schemaVersionType(), Integer.toString(ir.version())),
schemaIdAccessorType,
schemaVersionAccessorType,
semanticVersion);
}
private CharSequence generateDecoderFlyweightCode(final String className, final Token token)
{
final String headerClassName = formatClassName(ir.headerStructure().tokens().get(0).applicableTypeName());
final String methods =
" public " + className + " wrap(\n" +
" final " + readOnlyBuffer + " buffer,\n" +
" final int offset,\n" +
" final int actingBlockLength,\n" +
" final int actingVersion)\n" +
" {\n" +
" if (buffer != this.buffer)\n" +
" {\n" +
" this.buffer = buffer;\n" +
" }\n" +
" this.initialOffset = offset;\n" +
" this.offset = offset;\n" +
" this.actingBlockLength = actingBlockLength;\n" +
" this.actingVersion = actingVersion;\n" +
" limit(offset + actingBlockLength);\n\n" +
" return this;\n" +
" }\n\n" +
" public " + className + " wrapAndApplyHeader(\n" +
" final " + readOnlyBuffer + " buffer,\n" +
" final int offset,\n" +
" final " + headerClassName + "Decoder headerDecoder)\n" +
" {\n" +
" headerDecoder.wrap(buffer, offset);\n\n" +
" final int templateId = headerDecoder.templateId();\n" +
" if (TEMPLATE_ID != templateId)\n" +
" {\n" +
" throw new IllegalStateException(\"Invalid TEMPLATE_ID: \" + templateId);\n" +
" }\n\n" +
" return wrap(\n" +
" buffer,\n" +
" offset + " + headerClassName + "Decoder.ENCODED_LENGTH,\n" +
" headerDecoder.blockLength(),\n" +
" headerDecoder.version());\n" +
" }\n\n" +
" public " + className + " sbeRewind()\n" +
" {\n" +
" return wrap(buffer, initialOffset, actingBlockLength, actingVersion);\n" +
" }\n\n" +
" public int sbeDecodedLength()\n" +
" {\n" +
" final int currentLimit = limit();\n" +
" sbeSkip();\n" +
" final int decodedLength = encodedLength();\n" +
" limit(currentLimit);\n\n" +
" return decodedLength;\n" +
" }\n\n";
return generateFlyweightCode(DECODER, className, token, methods, readOnlyBuffer);
}
private CharSequence generateFlyweightCode(
final CodecType codecType,
final String className,
final Token token,
final String wrapMethod,
final String bufferImplementation)
{
final HeaderStructure headerStructure = ir.headerStructure();
final String blockLengthType = javaTypeName(headerStructure.blockLengthType());
final String blockLengthAccessorType = shouldGenerateInterfaces ? "int" : blockLengthType;
final String templateIdType = javaTypeName(headerStructure.templateIdType());
final String templateIdAccessorType = shouldGenerateInterfaces ? "int" : templateIdType;
final String schemaIdType = javaTypeName(headerStructure.schemaIdType());
final String schemaIdAccessorType = shouldGenerateInterfaces ? "int" : schemaIdType;
final String schemaVersionType = javaTypeName(headerStructure.schemaVersionType());
final String schemaVersionAccessorType = shouldGenerateInterfaces ? "int" : schemaVersionType;
final String semanticType = token.encoding().semanticType() == null ? "" : token.encoding().semanticType();
final String semanticVersion = ir.semanticVersion() == null ? "" : ir.semanticVersion();
final String actingFields = codecType == CodecType.ENCODER ?
"" :
" int actingBlockLength;\n" +
" int actingVersion;\n";
return String.format(
" public static final %1$s BLOCK_LENGTH = %2$s;\n" +
" public static final %3$s TEMPLATE_ID = %4$s;\n" +
" public static final %5$s SCHEMA_ID = %6$s;\n" +
" public static final %7$s SCHEMA_VERSION = %8$s;\n" +
" public static final String SEMANTIC_VERSION = \"%19$s\";\n" +
" public static final java.nio.ByteOrder BYTE_ORDER = java.nio.ByteOrder.%14$s;\n\n" +
" private final %9$s parentMessage = this;\n" +
" private %11$s buffer;\n" +
" private int initialOffset;\n" +
" private int offset;\n" +
" private int limit;\n" +
"%13$s" +
"\n" +
" public %15$s sbeBlockLength()\n" +
" {\n" +
" return BLOCK_LENGTH;\n" +
" }\n\n" +
" public %16$s sbeTemplateId()\n" +
" {\n" +
" return TEMPLATE_ID;\n" +
" }\n\n" +
" public %17$s sbeSchemaId()\n" +
" {\n" +
" return SCHEMA_ID;\n" +
" }\n\n" +
" public %18$s sbeSchemaVersion()\n" +
" {\n" +
" return SCHEMA_VERSION;\n" +
" }\n\n" +
" public String sbeSemanticType()\n" +
" {\n" +
" return \"%10$s\";\n" +
" }\n\n" +
" public %11$s buffer()\n" +
" {\n" +
" return buffer;\n" +
" }\n\n" +
" public int initialOffset()\n" +
" {\n" +
" return initialOffset;\n" +
" }\n\n" +
" public int offset()\n" +
" {\n" +
" return offset;\n" +
" }\n\n" +
"%12$s" +
" public int encodedLength()\n" +
" {\n" +
" return limit - offset;\n" +
" }\n\n" +
" public int limit()\n" +
" {\n" +
" return limit;\n" +
" }\n\n" +
" public void limit(final int limit)\n" +
" {\n" +
" this.limit = limit;\n" +
" }\n",
blockLengthType,
generateLiteral(headerStructure.blockLengthType(), Integer.toString(token.encodedLength())),
templateIdType,
generateLiteral(headerStructure.templateIdType(), Integer.toString(token.id())),
schemaIdType,
generateLiteral(headerStructure.schemaIdType(), Integer.toString(ir.id())),
schemaVersionType,
generateLiteral(headerStructure.schemaVersionType(), Integer.toString(ir.version())),
className,
semanticType,
bufferImplementation,
wrapMethod,
actingFields,
ir.byteOrder(),
blockLengthAccessorType,
templateIdAccessorType,
schemaIdAccessorType,
schemaVersionAccessorType,
semanticVersion);
}
private CharSequence generateEncoderFlyweightCode(final String className, final Token token)
{
final String wrapMethod =
" public " + className + " wrap(final " + mutableBuffer + " buffer, final int offset)\n" +
" {\n" +
" if (buffer != this.buffer)\n" +
" {\n" +
" this.buffer = buffer;\n" +
" }\n" +
" this.initialOffset = offset;\n" +
" this.offset = offset;\n" +
" limit(offset + BLOCK_LENGTH);\n\n" +
" return this;\n" +
" }\n\n";
final StringBuilder builder = new StringBuilder(
" public %1$s wrapAndApplyHeader(\n" +
" final %2$s buffer, final int offset, final %3$s headerEncoder)\n" +
" {\n" +
" headerEncoder\n" +
" .wrap(buffer, offset)");
for (final Token headerToken : ir.headerStructure().tokens())
{
if (!headerToken.isConstantEncoding())
{
switch (headerToken.name())
{
case "blockLength":
builder.append("\n .blockLength(BLOCK_LENGTH)");
break;
case "templateId":
builder.append("\n .templateId(TEMPLATE_ID)");
break;
case "schemaId":
builder.append("\n .schemaId(SCHEMA_ID)");
break;
case "version":
builder.append("\n .version(SCHEMA_VERSION)");
break;
}
}
}
builder.append(";\n\n return wrap(buffer, offset + %3$s.ENCODED_LENGTH);\n" + " }\n\n");
final String wrapAndApplyMethod = String.format(
builder.toString(),
className,
mutableBuffer,
formatClassName(ir.headerStructure().tokens().get(0).applicableTypeName() + "Encoder"));
return generateFlyweightCode(
CodecType.ENCODER, className, token, wrapMethod + wrapAndApplyMethod, mutableBuffer);
}
private void generateEncoderFields(
final StringBuilder sb, final String containingClassName, final List tokens, final String indent)
{
Generators.forEachField(
tokens,
(fieldToken, typeToken) ->
{
final String propertyName = formatPropertyName(fieldToken.name());
final String typeName = encoderName(typeToken.name());
generateFieldIdMethod(sb, fieldToken, indent);
generateFieldSinceVersionMethod(sb, fieldToken, indent);
generateEncodingOffsetMethod(sb, propertyName, fieldToken.offset(), indent);
generateEncodingLengthMethod(sb, propertyName, typeToken.encodedLength(), indent);
generateFieldMetaAttributeMethod(sb, fieldToken, indent);
switch (typeToken.signal())
{
case ENCODING:
generatePrimitiveEncoder(sb, containingClassName, propertyName, typeToken, indent);
break;
case BEGIN_ENUM:
generateEnumEncoder(sb, containingClassName, fieldToken, propertyName, typeToken, indent);
break;
case BEGIN_SET:
generateBitSetProperty(
sb, false, ENCODER, propertyName, fieldToken, typeToken, indent, typeName);
break;
case BEGIN_COMPOSITE:
generateCompositeProperty(
sb, false, ENCODER, propertyName, fieldToken, typeToken, indent, typeName);
break;
default:
break;
}
});
}
private void generateDecoderFields(final StringBuilder sb, final List tokens, final String indent)
{
Generators.forEachField(
tokens,
(fieldToken, typeToken) ->
{
final String propertyName = formatPropertyName(fieldToken.name());
final String typeName = decoderName(typeToken.name());
generateFieldIdMethod(sb, fieldToken, indent);
generateFieldSinceVersionMethod(sb, fieldToken, indent);
generateEncodingOffsetMethod(sb, propertyName, fieldToken.offset(), indent);
generateEncodingLengthMethod(sb, propertyName, typeToken.encodedLength(), indent);
generateFieldMetaAttributeMethod(sb, fieldToken, indent);
switch (typeToken.signal())
{
case ENCODING:
generatePrimitiveDecoder(sb, false, propertyName, fieldToken, typeToken, indent);
break;
case BEGIN_ENUM:
generateEnumDecoder(sb, false, fieldToken, propertyName, typeToken, indent);
break;
case BEGIN_SET:
generateBitSetProperty(
sb, false, DECODER, propertyName, fieldToken, typeToken, indent, typeName);
break;
case BEGIN_COMPOSITE:
generateCompositeProperty(
sb, false, DECODER, propertyName, fieldToken, typeToken, indent, typeName);
break;
default:
break;
}
});
}
private static void generateFieldIdMethod(final StringBuilder sb, final Token token, final String indent)
{
final String propertyName = formatPropertyName(token.name());
sb.append("\n")
.append(indent).append(" public static int ").append(propertyName).append("Id()\n")
.append(indent).append(" {\n")
.append(indent).append(" return ").append(token.id()).append(";\n")
.append(indent).append(" }\n");
}
private static void generateEncodingOffsetMethod(
final StringBuilder sb, final String name, final int offset, final String indent)
{
final String propertyName = formatPropertyName(name);
sb.append("\n")
.append(indent).append(" public static int ").append(propertyName).append("EncodingOffset()\n")
.append(indent).append(" {\n")
.append(indent).append(" return ").append(offset).append(";\n")
.append(indent).append(" }\n");
}
private static void generateEncodingLengthMethod(
final StringBuilder sb, final String name, final int length, final String indent)
{
final String propertyName = formatPropertyName(name);
sb.append("\n")
.append(indent).append(" public static int ").append(propertyName).append("EncodingLength()\n")
.append(indent).append(" {\n")
.append(indent).append(" return ").append(length).append(";\n")
.append(indent).append(" }\n");
}
private static void generateFieldSinceVersionMethod(final StringBuilder sb, final Token token, final String indent)
{
final String propertyName = formatPropertyName(token.name());
sb.append("\n")
.append(indent).append(" public static int ").append(propertyName).append("SinceVersion()\n")
.append(indent).append(" {\n")
.append(indent).append(" return ").append(token.version()).append(";\n")
.append(indent).append(" }\n");
}
private static void generateFieldMetaAttributeMethod(final StringBuilder sb, final Token token, final String indent)
{
final Encoding encoding = token.encoding();
final String epoch = encoding.epoch() == null ? "" : encoding.epoch();
final String timeUnit = encoding.timeUnit() == null ? "" : encoding.timeUnit();
final String semanticType = encoding.semanticType() == null ? "" : encoding.semanticType();
final String presence = encoding.presence().toString().toLowerCase();
final String propertyName = formatPropertyName(token.name());
sb.append("\n")
.append(indent).append(" public static String ")
.append(propertyName).append("MetaAttribute(final MetaAttribute metaAttribute)\n")
.append(indent).append(" {\n")
.append(indent).append(" if (MetaAttribute.PRESENCE == metaAttribute)\n")
.append(indent).append(" {\n")
.append(indent).append(" return \"").append(presence).append("\";\n")
.append(indent).append(" }\n");
if (!Strings.isEmpty(epoch))
{
sb.append(indent).append(" if (MetaAttribute.EPOCH == metaAttribute)\n")
.append(indent).append(" {\n")
.append(indent).append(" return \"").append(epoch).append("\";\n")
.append(indent).append(" }\n");
}
if (!Strings.isEmpty(timeUnit))
{
sb.append(indent).append(" if (MetaAttribute.TIME_UNIT == metaAttribute)\n")
.append(indent).append(" {\n")
.append(indent).append(" return \"").append(timeUnit).append("\";\n")
.append(indent).append(" }\n");
}
if (!Strings.isEmpty(semanticType))
{
sb.append(indent).append(" if (MetaAttribute.SEMANTIC_TYPE == metaAttribute)\n")
.append(indent).append(" {\n")
.append(indent).append(" return \"").append(semanticType).append("\";\n")
.append(indent).append(" }\n");
}
sb.append("\n")
.append(indent).append(" return \"\";\n")
.append(indent).append(" }\n");
}
private void generateEnumDecoder(
final StringBuilder sb,
final boolean inComposite,
final Token fieldToken,
final String propertyName,
final Token typeToken,
final String indent)
{
final String enumName = formatClassName(typeToken.applicableTypeName());
final Encoding encoding = typeToken.encoding();
final String javaTypeName = javaTypeName(encoding.primitiveType());
if (fieldToken.isConstantEncoding())
{
final String enumValueStr = fieldToken.encoding().constValue().toString();
new Formatter(sb).format(
"\n" +
indent + " public %s %sRaw()\n" +
indent + " {\n" +
indent + " return %s.value();\n" +
indent + " }\n\n",
javaTypeName,
propertyName,
enumValueStr);
new Formatter(sb).format(
"\n" +
indent + " public %s %s()\n" +
indent + " {\n" +
indent + " return %s;\n" +
indent + " }\n\n",
enumName,
propertyName,
enumValueStr);
}
else
{
final String rawGetStr = generateGet(
encoding.primitiveType(), "offset + " + typeToken.offset(), byteOrderString(encoding));
new Formatter(sb).format(
"\n" +
indent + " public %s %sRaw()\n" +
indent + " {\n" +
"%s" +
indent + " return %s;\n" +
indent + " }\n",
javaTypeName,
formatPropertyName(propertyName),
generateFieldNotPresentCondition(inComposite, fieldToken.version(), encoding, indent),
rawGetStr);
new Formatter(sb).format(
"\n" +
indent + " public %s %s()\n" +
indent + " {\n" +
"%s" +
indent + " return %s.get(%s);\n" +
indent + " }\n\n",
enumName,
propertyName,
generatePropertyNotPresentCondition(inComposite, DECODER, fieldToken, enumName, indent),
enumName,
rawGetStr);
}
}
private void generateEnumEncoder(
final StringBuilder sb,
final String containingClassName,
final Token fieldToken,
final String propertyName,
final Token typeToken,
final String indent)
{
if (!fieldToken.isConstantEncoding())
{
final String enumName = formatClassName(typeToken.applicableTypeName());
final Encoding encoding = typeToken.encoding();
final int offset = typeToken.offset();
final String byteOrderString = byteOrderString(encoding);
new Formatter(sb).format("\n" +
indent + " public %s %s(final %s value)\n" +
indent + " {\n" +
indent + " %s;\n" +
indent + " return this;\n" +
indent + " }\n",
formatClassName(containingClassName),
propertyName,
enumName,
generatePut(encoding.primitiveType(), "offset + " + offset, "value.value()", byteOrderString));
}
}
private void generateBitSetProperty(
final StringBuilder sb,
final boolean inComposite,
final CodecType codecType,
final String propertyName,
final Token propertyToken,
final Token bitsetToken,
final String indent,
final String bitSetName)
{
new Formatter(sb).format("\n" +
indent + " private final %s %s = new %s();\n",
bitSetName,
propertyName,
bitSetName);
generateFlyweightPropertyJavadoc(sb, indent + INDENT, propertyToken, bitSetName);
new Formatter(sb).format("\n" +
indent + " public %s %s()\n" +
indent + " {\n" +
"%s" +
indent + " %s.wrap(buffer, offset + %d);\n" +
indent + " return %s;\n" +
indent + " }\n",
bitSetName,
propertyName,
generatePropertyNotPresentCondition(inComposite, codecType, propertyToken, null, indent),
propertyName,
bitsetToken.offset(),
propertyName);
}
private void generateCompositeProperty(
final StringBuilder sb,
final boolean inComposite,
final CodecType codecType,
final String propertyName,
final Token propertyToken,
final Token compositeToken,
final String indent,
final String compositeName)
{
new Formatter(sb).format("\n" +
indent + " private final %s %s = new %s();\n",
compositeName,
propertyName,
compositeName);
generateFlyweightPropertyJavadoc(sb, indent + INDENT, propertyToken, compositeName);
new Formatter(sb).format("\n" +
indent + " public %s %s()\n" +
indent + " {\n" +
"%s" +
indent + " %s.wrap(buffer, offset + %d);\n" +
indent + " return %s;\n" +
indent + " }\n",
compositeName,
propertyName,
generatePropertyNotPresentCondition(inComposite, codecType, propertyToken, null, indent),
propertyName,
compositeToken.offset(),
propertyName);
}
private String generateGet(final PrimitiveType type, final String index, final String byteOrder)
{
switch (type)
{
case CHAR:
case INT8:
return "buffer.getByte(" + index + ")";
case UINT8:
return "((short)(buffer.getByte(" + index + ") & 0xFF))";
case INT16:
return "buffer.getShort(" + index + byteOrder + ")";
case UINT16:
return "(buffer.getShort(" + index + byteOrder + ") & 0xFFFF)";
case INT32:
return "buffer.getInt(" + index + byteOrder + ")";
case UINT32:
return "(buffer.getInt(" + index + byteOrder + ") & 0xFFFF_FFFFL)";
case FLOAT:
return "buffer.getFloat(" + index + byteOrder + ")";
case INT64:
case UINT64:
return "buffer.getLong(" + index + byteOrder + ")";
case DOUBLE:
return "buffer.getDouble(" + index + byteOrder + ")";
default:
break;
}
throw new IllegalArgumentException("primitive type not supported: " + type);
}
private String generatePut(
final PrimitiveType type, final String index, final String value, final String byteOrder)
{
switch (type)
{
case CHAR:
case INT8:
return "buffer.putByte(" + index + ", " + value + ")";
case UINT8:
return "buffer.putByte(" + index + ", (byte)" + value + ")";
case INT16:
return "buffer.putShort(" + index + ", " + value + byteOrder + ")";
case UINT16:
return "buffer.putShort(" + index + ", (short)" + value + byteOrder + ")";
case INT32:
return "buffer.putInt(" + index + ", " + value + byteOrder + ")";
case UINT32:
return "buffer.putInt(" + index + ", (int)" + value + byteOrder + ")";
case FLOAT:
return "buffer.putFloat(" + index + ", " + value + byteOrder + ")";
case INT64:
case UINT64:
return "buffer.putLong(" + index + ", " + value + byteOrder + ")";
case DOUBLE:
return "buffer.putDouble(" + index + ", " + value + byteOrder + ")";
default:
break;
}
throw new IllegalArgumentException("primitive type not supported: " + type);
}
private String generateChoiceIsEmpty(final PrimitiveType type)
{
return "\n" +
" public boolean isEmpty()\n" +
" {\n" +
" return " + generateChoiceIsEmptyInner(type) + ";\n" +
" }\n";
}
private String generateChoiceIsEmptyInner(final PrimitiveType type)
{
switch (type)
{
case UINT8:
return "0 == buffer.getByte(offset)";
case UINT16:
return "0 == buffer.getShort(offset)";
case UINT32:
return "0 == buffer.getInt(offset)";
case UINT64:
return "0 == buffer.getLong(offset)";
default:
break;
}
throw new IllegalArgumentException("primitive type not supported: " + type);
}
private String generateChoiceGet(final PrimitiveType type, final String bitIndex, final String byteOrder)
{
switch (type)
{
case UINT8:
return "0 != (buffer.getByte(offset) & (1 << " + bitIndex + "))";
case UINT16:
return "0 != (buffer.getShort(offset" + byteOrder + ") & (1 << " + bitIndex + "))";
case UINT32:
return "0 != (buffer.getInt(offset" + byteOrder + ") & (1 << " + bitIndex + "))";
case UINT64:
return "0 != (buffer.getLong(offset" + byteOrder + ") & (1L << " + bitIndex + "))";
default:
break;
}
throw new IllegalArgumentException("primitive type not supported: " + type);
}
private String generateStaticChoiceGet(final PrimitiveType type, final String bitIndex)
{
switch (type)
{
case UINT8:
case UINT16:
case UINT32:
return "0 != (value & (1 << " + bitIndex + "))";
case UINT64:
return "0 != (value & (1L << " + bitIndex + "))";
default:
break;
}
throw new IllegalArgumentException("primitive type not supported: " + type);
}
private String generateChoicePut(final PrimitiveType type, final String bitIdx, final String byteOrder)
{
switch (type)
{
case UINT8:
return
" byte bits = buffer.getByte(offset);\n" +
" bits = (byte)(value ? bits | (1 << " + bitIdx + ") : bits & ~(1 << " + bitIdx + "));\n" +
" buffer.putByte(offset, bits);";
case UINT16:
return
" short bits = buffer.getShort(offset" + byteOrder + ");\n" +
" bits = (short)(value ? bits | (1 << " + bitIdx + ") : bits & ~(1 << " + bitIdx + "));\n" +
" buffer.putShort(offset, bits" + byteOrder + ");";
case UINT32:
return
" int bits = buffer.getInt(offset" + byteOrder + ");\n" +
" bits = value ? bits | (1 << " + bitIdx + ") : bits & ~(1 << " + bitIdx + ");\n" +
" buffer.putInt(offset, bits" + byteOrder + ");";
case UINT64:
return
" long bits = buffer.getLong(offset" + byteOrder + ");\n" +
" bits = value ? bits | (1L << " + bitIdx + ") : bits & ~(1L << " + bitIdx + ");\n" +
" buffer.putLong(offset, bits" + byteOrder + ");";
default:
break;
}
throw new IllegalArgumentException("primitive type not supported: " + type);
}
private String generateStaticChoicePut(final PrimitiveType type, final String bitIdx)
{
switch (type)
{
case UINT8:
return
" return (byte)(value ? bits | (1 << " + bitIdx + ") : bits & ~(1 << " + bitIdx + "));\n";
case UINT16:
return
" return (short)(value ? bits | (1 << " + bitIdx + ") : bits & ~(1 << " + bitIdx + "));\n";
case UINT32:
return
" return value ? bits | (1 << " + bitIdx + ") : bits & ~(1 << " + bitIdx + ");\n";
case UINT64:
return
" return value ? bits | (1L << " + bitIdx + ") : bits & ~(1L << " + bitIdx + ");\n";
default:
break;
}
throw new IllegalArgumentException("primitive type not supported: " + type);
}
private void generateEncoderDisplay(final StringBuilder sb, final String decoderName)
{
appendToString(sb);
sb.append('\n');
append(sb, INDENT, "public StringBuilder appendTo(final StringBuilder builder)");
append(sb, INDENT, "{");
append(sb, INDENT, " if (null == buffer)");
append(sb, INDENT, " {");
append(sb, INDENT, " return builder;");
append(sb, INDENT, " }");
sb.append('\n');
append(sb, INDENT, " final " + decoderName + " decoder = new " + decoderName + "();");
append(sb, INDENT, " decoder.wrap(buffer, initialOffset, BLOCK_LENGTH, SCHEMA_VERSION);");
sb.append('\n');
append(sb, INDENT, " return decoder.appendTo(builder);");
append(sb, INDENT, "}");
}
private CharSequence generateCompositeEncoderDisplay(final String decoderName)
{
final StringBuilder sb = new StringBuilder();
appendToString(sb);
sb.append('\n');
append(sb, INDENT, "public StringBuilder appendTo(final StringBuilder builder)");
append(sb, INDENT, "{");
append(sb, INDENT, " if (null == buffer)");
append(sb, INDENT, " {");
append(sb, INDENT, " return builder;");
append(sb, INDENT, " }");
sb.append('\n');
append(sb, INDENT, " final " + decoderName + " decoder = new " + decoderName + "();");
append(sb, INDENT, " decoder.wrap(buffer, offset);");
sb.append('\n');
append(sb, INDENT, " return decoder.appendTo(builder);");
append(sb, INDENT, "}");
return sb;
}
private CharSequence generateCompositeDecoderDisplay(final List tokens)
{
final StringBuilder sb = new StringBuilder();
appendToString(sb);
sb.append('\n');
append(sb, INDENT, "public StringBuilder appendTo(final StringBuilder builder)");
append(sb, INDENT, "{");
append(sb, INDENT, " if (null == buffer)");
append(sb, INDENT, " {");
append(sb, INDENT, " return builder;");
append(sb, INDENT, " }");
sb.append('\n');
Separator.BEGIN_COMPOSITE.appendToGeneratedBuilder(sb, INDENT + INDENT);
int lengthBeforeLastGeneratedSeparator = -1;
for (int i = 1, end = tokens.size() - 1; i < end;)
{
final Token encodingToken = tokens.get(i);
final String propertyName = formatPropertyName(encodingToken.name());
lengthBeforeLastGeneratedSeparator = writeTokenDisplay(propertyName, encodingToken, sb, INDENT + INDENT);
i += encodingToken.componentTokenCount();
}
if (-1 != lengthBeforeLastGeneratedSeparator)
{
sb.setLength(lengthBeforeLastGeneratedSeparator);
}
Separator.END_COMPOSITE.appendToGeneratedBuilder(sb, INDENT + INDENT);
sb.append('\n');
append(sb, INDENT, " return builder;");
append(sb, INDENT, "}");
return sb;
}
private CharSequence generateChoiceDisplay(final List tokens)
{
final StringBuilder sb = new StringBuilder();
appendToString(sb);
sb.append('\n');
append(sb, INDENT, "public StringBuilder appendTo(final StringBuilder builder)");
append(sb, INDENT, "{");
Separator.BEGIN_SET.appendToGeneratedBuilder(sb, INDENT + INDENT);
append(sb, INDENT, " boolean atLeastOne = false;");
for (final Token token : tokens)
{
if (token.signal() == Signal.CHOICE)
{
final String choiceName = formatPropertyName(token.name());
append(sb, INDENT, " if (" + choiceName + "())");
append(sb, INDENT, " {");
append(sb, INDENT, " if (atLeastOne)");
append(sb, INDENT, " {");
Separator.ENTRY.appendToGeneratedBuilder(sb, INDENT + INDENT + INDENT + INDENT);
append(sb, INDENT, " }");
append(sb, INDENT, " builder.append(\"" + choiceName + "\");");
append(sb, INDENT, " atLeastOne = true;");
append(sb, INDENT, " }");
}
}
Separator.END_SET.appendToGeneratedBuilder(sb, INDENT + INDENT);
sb.append('\n');
append(sb, INDENT, " return builder;");
append(sb, INDENT, "}");
return sb;
}
private void generateDecoderDisplay(
final StringBuilder sb,
final String name,
final List tokens,
final List groups,
final List varData)
{
appendMessageToString(sb, decoderName(name));
sb.append('\n');
append(sb, INDENT, "public StringBuilder appendTo(final StringBuilder builder)");
append(sb, INDENT, "{");
append(sb, INDENT, " if (null == buffer)");
append(sb, INDENT, " {");
append(sb, INDENT, " return builder;");
append(sb, INDENT, " }");
sb.append('\n');
append(sb, INDENT, " final int originalLimit = limit();");
append(sb, INDENT, " limit(initialOffset + actingBlockLength);");
append(sb, INDENT, " builder.append(\"[" + name + "](sbeTemplateId=\");");
append(sb, INDENT, " builder.append(TEMPLATE_ID);");
append(sb, INDENT, " builder.append(\"|sbeSchemaId=\");");
append(sb, INDENT, " builder.append(SCHEMA_ID);");
append(sb, INDENT, " builder.append(\"|sbeSchemaVersion=\");");
append(sb, INDENT, " if (parentMessage.actingVersion != SCHEMA_VERSION)");
append(sb, INDENT, " {");
append(sb, INDENT, " builder.append(parentMessage.actingVersion);");
append(sb, INDENT, " builder.append('/');");
append(sb, INDENT, " }");
append(sb, INDENT, " builder.append(SCHEMA_VERSION);");
append(sb, INDENT, " builder.append(\"|sbeBlockLength=\");");
append(sb, INDENT, " if (actingBlockLength != BLOCK_LENGTH)");
append(sb, INDENT, " {");
append(sb, INDENT, " builder.append(actingBlockLength);");
append(sb, INDENT, " builder.append('/');");
append(sb, INDENT, " }");
append(sb, INDENT, " builder.append(BLOCK_LENGTH);");
append(sb, INDENT, " builder.append(\"):\");");
appendDecoderDisplay(sb, tokens, groups, varData, INDENT + INDENT);
sb.append('\n');
append(sb, INDENT, " limit(originalLimit);");
sb.append('\n');
append(sb, INDENT, " return builder;");
append(sb, INDENT, "}");
}
private void appendGroupInstanceDecoderDisplay(
final StringBuilder sb,
final List fields,
final List groups,
final List varData,
final String baseIndent)
{
final String indent = baseIndent + INDENT;
sb.append('\n');
append(sb, indent, "public StringBuilder appendTo(final StringBuilder builder)");
append(sb, indent, "{");
append(sb, indent, " if (null == buffer)");
append(sb, indent, " {");
append(sb, indent, " return builder;");
append(sb, indent, " }");
sb.append('\n');
Separator.BEGIN_COMPOSITE.appendToGeneratedBuilder(sb, indent + INDENT);
appendDecoderDisplay(sb, fields, groups, varData, indent + INDENT);
Separator.END_COMPOSITE.appendToGeneratedBuilder(sb, indent + INDENT);
sb.append('\n');
append(sb, indent, " return builder;");
append(sb, indent, "}");
}
private void appendDecoderDisplay(
final StringBuilder sb,
final List fields,
final List groups,
final List varData,
final String indent)
{
int lengthBeforeLastGeneratedSeparator = -1;
for (int i = 0, size = fields.size(); i < size;)
{
final Token fieldToken = fields.get(i);
if (fieldToken.signal() == Signal.BEGIN_FIELD)
{
final Token encodingToken = fields.get(i + 1);
final String fieldName = formatPropertyName(fieldToken.name());
lengthBeforeLastGeneratedSeparator = writeTokenDisplay(fieldName, encodingToken, sb, indent);
i += fieldToken.componentTokenCount();
}
else
{
++i;
}
}
for (int i = 0, size = groups.size(); i < size; i++)
{
final Token groupToken = groups.get(i);
if (groupToken.signal() != Signal.BEGIN_GROUP)
{
throw new IllegalStateException("tokens must begin with BEGIN_GROUP: token=" + groupToken);
}
final String groupName = formatPropertyName(groupToken.name());
final String groupDecoderName = decoderName(groupToken.name());
append(
sb, indent, "builder.append(\"" + groupName + Separator.KEY_VALUE + Separator.BEGIN_GROUP + "\");");
append(sb, indent, "final int " + groupName + "OriginalOffset = " + groupName + ".offset;");
append(sb, indent, "final int " + groupName + "OriginalIndex = " + groupName + ".index;");
append(sb, indent, "final " + groupDecoderName + " " + groupName + " = this." + groupName + "();");
append(sb, indent, "if (" + groupName + ".count() > 0)");
append(sb, indent, "{");
append(sb, indent, " while (" + groupName + ".hasNext())");
append(sb, indent, " {");
append(sb, indent, " " + groupName + ".next().appendTo(builder);");
Separator.ENTRY.appendToGeneratedBuilder(sb, indent + INDENT + INDENT);
append(sb, indent, " }");
append(sb, indent, " builder.setLength(builder.length() - 1);");
append(sb, indent, "}");
append(sb, indent, groupName + ".offset = " + groupName + "OriginalOffset;");
append(sb, indent, groupName + ".index = " + groupName + "OriginalIndex;");
Separator.END_GROUP.appendToGeneratedBuilder(sb, indent);
lengthBeforeLastGeneratedSeparator = sb.length();
Separator.FIELD.appendToGeneratedBuilder(sb, indent);
i = findEndSignal(groups, i, Signal.END_GROUP, groupToken.name());
}
for (int i = 0, size = varData.size(); i < size;)
{
final Token varDataToken = varData.get(i);
if (varDataToken.signal() != Signal.BEGIN_VAR_DATA)
{
throw new IllegalStateException("tokens must begin with BEGIN_VAR_DATA: token=" + varDataToken);
}
final String characterEncoding = varData.get(i + 3).encoding().characterEncoding();
final String varDataName = formatPropertyName(varDataToken.name());
append(sb, indent, "builder.append(\"" + varDataName + Separator.KEY_VALUE + "\");");
if (null == characterEncoding)
{
final String name = Generators.toUpperFirstChar(varDataToken.name());
append(sb, indent, "builder.append(skip" + name + "()).append(\" bytes of raw data\");");
}
else
{
if (isAsciiEncoding(characterEncoding))
{
append(sb, indent, "builder.append('\\'');");
append(sb, indent, formatGetterName(varDataToken.name()) + "(builder);");
append(sb, indent, "builder.append('\\'');");
}
else
{
append(sb, indent, "builder.append('\\'').append(" + varDataName + "()).append('\\'');");
}
}
lengthBeforeLastGeneratedSeparator = sb.length();
Separator.FIELD.appendToGeneratedBuilder(sb, indent);
i += varDataToken.componentTokenCount();
}
if (-1 != lengthBeforeLastGeneratedSeparator)
{
sb.setLength(lengthBeforeLastGeneratedSeparator);
}
}
private int writeTokenDisplay(
final String fieldName, final Token typeToken, final StringBuilder sb, final String indent)
{
if (typeToken.encodedLength() <= 0 || typeToken.isConstantEncoding())
{
return -1;
}
append(sb, indent, "builder.append(\"" + fieldName + Separator.KEY_VALUE + "\");");
switch (typeToken.signal())
{
case ENCODING:
if (typeToken.arrayLength() > 1)
{
if (typeToken.encoding().primitiveType() == PrimitiveType.CHAR)
{
append(sb, indent,
"for (int i = 0; i < " + fieldName + "Length() && this." + fieldName + "(i) > 0; i++)");
append(sb, indent, "{");
append(sb, indent, " builder.append((char)this." + fieldName + "(i));");
append(sb, indent, "}");
}
else
{
Separator.BEGIN_ARRAY.appendToGeneratedBuilder(sb, indent);
append(sb, indent, "if (" + fieldName + "Length() > 0)");
append(sb, indent, "{");
append(sb, indent, " for (int i = 0; i < " + fieldName + "Length(); i++)");
append(sb, indent, " {");
append(sb, indent, " builder.append(this." + fieldName + "(i));");
Separator.ENTRY.appendToGeneratedBuilder(sb, indent + INDENT + INDENT);
append(sb, indent, " }");
append(sb, indent, " builder.setLength(builder.length() - 1);");
append(sb, indent, "}");
Separator.END_ARRAY.appendToGeneratedBuilder(sb, indent);
}
}
else
{
// have to duplicate because of checkstyle :/
append(sb, indent, "builder.append(this." + fieldName + "());");
}
break;
case BEGIN_ENUM:
append(sb, indent, "builder.append(this." + fieldName + "());");
break;
case BEGIN_SET:
append(sb, indent, "this." + fieldName + "().appendTo(builder);");
break;
case BEGIN_COMPOSITE:
{
final String typeName = formatClassName(decoderName(typeToken.applicableTypeName()));
append(sb, indent, "final " + typeName + " " + fieldName + " = this." + fieldName + "();");
append(sb, indent, "if (" + fieldName + " != null)");
append(sb, indent, "{");
append(sb, indent, " " + fieldName + ".appendTo(builder);");
append(sb, indent, "}");
append(sb, indent, "else");
append(sb, indent, "{");
append(sb, indent, " builder.append(\"null\");");
append(sb, indent, "}");
break;
}
default:
break;
}
final int lengthBeforeFieldSeparator = sb.length();
Separator.FIELD.appendToGeneratedBuilder(sb, indent);
return lengthBeforeFieldSeparator;
}
private void appendToString(final StringBuilder sb)
{
sb.append('\n');
append(sb, INDENT, "public String toString()");
append(sb, INDENT, "{");
append(sb, INDENT, " if (null == buffer)");
append(sb, INDENT, " {");
append(sb, INDENT, " return \"\";");
append(sb, INDENT, " }");
sb.append('\n');
append(sb, INDENT, " return appendTo(new StringBuilder()).toString();");
append(sb, INDENT, "}");
}
private void appendMessageToString(final StringBuilder sb, final String decoderName)
{
sb.append('\n');
append(sb, INDENT, "public String toString()");
append(sb, INDENT, "{");
append(sb, INDENT, " if (null == buffer)");
append(sb, INDENT, " {");
append(sb, INDENT, " return \"\";");
append(sb, INDENT, " }");
sb.append('\n');
append(sb, INDENT, " final " + decoderName + " decoder = new " + decoderName + "();");
append(sb, INDENT, " decoder.wrap(buffer, initialOffset, actingBlockLength, actingVersion);");
sb.append('\n');
append(sb, INDENT, " return decoder.appendTo(new StringBuilder()).toString();");
append(sb, INDENT, "}");
}
private void generateMessageLength(
final StringBuilder sb,
final String className,
final boolean isParent,
final List groups,
final List varData,
final String baseIndent)
{
final String methodIndent = baseIndent + INDENT;
final String bodyIndent = methodIndent + INDENT;
append(sb, methodIndent, "");
append(sb, methodIndent, "public " + className + " sbeSkip()");
append(sb, methodIndent, "{");
if (isParent)
{
append(sb, bodyIndent, "sbeRewind();");
}
for (int i = 0, size = groups.size(); i < size; i++)
{
final Token groupToken = groups.get(i);
if (groupToken.signal() != Signal.BEGIN_GROUP)
{
throw new IllegalStateException("tokens must begin with BEGIN_GROUP: token=" + groupToken);
}
final String groupName = formatPropertyName(groupToken.name());
final String groupDecoderName = decoderName(groupToken.name());
append(sb, bodyIndent, groupDecoderName + " " + groupName + " = this." + groupName + "();");
append(sb, bodyIndent, "if (" + groupName + ".count() > 0)");
append(sb, bodyIndent, "{");
append(sb, bodyIndent, " while (" + groupName + ".hasNext())");
append(sb, bodyIndent, " {");
append(sb, bodyIndent, " " + groupName + ".next();");
append(sb, bodyIndent, " " + groupName + ".sbeSkip();");
append(sb, bodyIndent, " }");
append(sb, bodyIndent, "}");
i = findEndSignal(groups, i, Signal.END_GROUP, groupToken.name());
}
for (int i = 0, size = varData.size(); i < size;)
{
final Token varDataToken = varData.get(i);
if (varDataToken.signal() != Signal.BEGIN_VAR_DATA)
{
throw new IllegalStateException("tokens must begin with BEGIN_VAR_DATA: token=" + varDataToken);
}
final String varDataName = formatPropertyName(varDataToken.name());
append(sb, bodyIndent, "skip" + Generators.toUpperFirstChar(varDataName) + "();");
i += varDataToken.componentTokenCount();
}
sb.append('\n');
append(sb, bodyIndent, "return this;");
append(sb, methodIndent, "}");
}
private static String validateBufferImplementation(
final String fullyQualifiedBufferImplementation, final Class> bufferClass)
{
Verify.notNull(fullyQualifiedBufferImplementation, "fullyQualifiedBufferImplementation");
try
{
final Class> clazz = Class.forName(fullyQualifiedBufferImplementation);
if (!bufferClass.isAssignableFrom(clazz))
{
throw new IllegalArgumentException(
fullyQualifiedBufferImplementation + " doesn't implement " + bufferClass.getName());
}
return clazz.getSimpleName();
}
catch (final ClassNotFoundException ex)
{
throw new IllegalArgumentException("Unable to find " + fullyQualifiedBufferImplementation, ex);
}
}
private String encoderName(final String className)
{
return formatClassName(className) + "Encoder";
}
private String decoderName(final String className)
{
return formatClassName(className) + "Decoder";
}
private String implementsInterface(final String interfaceName)
{
return shouldGenerateInterfaces ? " implements " + interfaceName : "";
}
}