uk.co.real_logic.sbe.generation.java.JavaGenerator Maven / Gradle / Ivy
/*
* Copyright 2013 Real Logic Ltd.
*
* 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
*
* http://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 uk.co.real_logic.agrona.DirectBuffer;
import uk.co.real_logic.agrona.MutableDirectBuffer;
import uk.co.real_logic.agrona.Verify;
import uk.co.real_logic.agrona.generation.OutputManager;
import uk.co.real_logic.sbe.PrimitiveType;
import uk.co.real_logic.sbe.generation.CodeGenerator;
import uk.co.real_logic.sbe.ir.*;
import java.io.IOException;
import java.io.Writer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Function;
import static uk.co.real_logic.sbe.generation.java.JavaUtil.*;
import static uk.co.real_logic.sbe.ir.GenerationUtil.*;
public class JavaGenerator implements CodeGenerator
{
private static final String META_ATTRIBUTE_ENUM = "MetaAttribute";
private static final String BASE_INDENT = "";
private static final String INDENT = " ";
private final Ir ir;
private final OutputManager outputManager;
private final String fullMutableBuffer;
private final String mutableBuffer;
private final String fullReadOnlyBuffer;
private final String readOnlyBuffer;
public JavaGenerator(
final Ir ir,
final String mutableBuffer,
final String readOnlyBuffer,
final OutputManager outputManager)
throws IOException
{
Verify.notNull(ir, "ir");
Verify.notNull(outputManager, "outputManager");
this.ir = ir;
this.outputManager = outputManager;
this.mutableBuffer = validateBufferImplementation(mutableBuffer, MutableDirectBuffer.class);
this.fullMutableBuffer = mutableBuffer;
this.readOnlyBuffer = validateBufferImplementation(readOnlyBuffer, DirectBuffer.class);
this.fullReadOnlyBuffer = readOnlyBuffer;
}
private String validateBufferImplementation(
final String fullyQualifiedBufferImplementation, final Class> bufferClass)
{
Verify.notNull(fullyQualifiedBufferImplementation, "fullyQualifiedBufferImplementation");
try
{
final Class> cls = Class.forName(fullyQualifiedBufferImplementation);
if (!bufferClass.isAssignableFrom(cls))
{
throw new IllegalArgumentException(
fullyQualifiedBufferImplementation + " doesn't implement " + bufferClass.getName());
}
return cls.getSimpleName();
}
catch (final ClassNotFoundException ex)
{
throw new IllegalArgumentException(
"Unable to validate " + fullyQualifiedBufferImplementation + " because it can't be found", ex);
}
}
private String encoderName(final String className)
{
return className + "Encoder";
}
private String decoderName(final String className)
{
return className + "Decoder";
}
public void generateMessageHeaderStub() throws IOException
{
final List tokens = ir.headerStructure().tokens();
final Token firstToken = tokens.get(0);
try (final Writer out = outputManager.createOutput(MESSAGE_HEADER_ENCODER_TYPE))
{
generateFixedFlyweightHeader(firstToken, MESSAGE_HEADER_ENCODER_TYPE, out, mutableBuffer, fullMutableBuffer);
out.append(concatEncodingTokens(tokens,
(token) -> generatePrimitiveEncoder(MESSAGE_HEADER_ENCODER_TYPE, token.name(), token, BASE_INDENT)));
out.append("}\n");
}
try (final Writer out = outputManager.createOutput(MESSAGE_HEADER_DECODER_TYPE))
{
generateFixedFlyweightHeader(firstToken, MESSAGE_HEADER_DECODER_TYPE, out, readOnlyBuffer, fullReadOnlyBuffer);
out.append(concatEncodingTokens(tokens,
(token) -> generatePrimitiveDecoder(token.name(), token, BASE_INDENT)));
out.append("}\n");
}
}
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;
}
}
}
public void generate() throws IOException
{
generateMessageHeaderStub();
generateTypeStubs();
for (final List tokens : ir.messages())
{
final Token msgToken = tokens.get(0);
final List messageBody = getMessageBody(tokens);
int offset = 0;
final List rootFields = new ArrayList<>();
offset = collectRootFields(messageBody, offset, rootFields);
final List groups = new ArrayList<>();
offset = collectGroups(messageBody, offset, groups);
final List varData = messageBody.subList(offset, messageBody.size());
generateDecoder(groups, rootFields, varData, msgToken);
generateEncoder(groups, rootFields, varData, msgToken);
}
}
private void generateEncoder(
final List groups,
final List rootFields,
final List varData,
final Token msgToken) throws IOException
{
final String className = formatClassName(encoderName(msgToken.name()));
try (final Writer out = outputManager.createOutput(className))
{
out.append(generateFileHeader(ir.applicableNamespace(), fullMutableBuffer));
generateAnnotations(className, groups, out, 0, this::encoderName);
out.append(generateClassDeclaration(className));
out.append(generateEncoderFlyweightCode(className, msgToken));
out.append(generateEncoderFields(className, rootFields, BASE_INDENT));
final StringBuilder sb = new StringBuilder();
generateEncoderGroups(sb, className, groups, 0, BASE_INDENT);
out.append(sb);
out.append(generateVarDataEncoders(className, varData));
out.append("}\n");
}
}
private void generateDecoder(
final List groups,
final List rootFields,
final List varData,
final Token msgToken) throws IOException
{
final String className = formatClassName(decoderName(msgToken.name()));
try (final Writer out = outputManager.createOutput(className))
{
out.append(generateFileHeader(ir.applicableNamespace(), fullReadOnlyBuffer));
generateAnnotations(className, groups, out, 0, this::decoderName);
out.append(generateClassDeclaration(className));
out.append(generateDecoderFlyweightCode(className, msgToken));
out.append(generateDecoderFields(rootFields, BASE_INDENT));
final StringBuilder sb = new StringBuilder();
generateDecoderGroups(sb, className, groups, 0, BASE_INDENT);
out.append(sb);
out.append(generateVarDataDecoders(varData));
out.append("}\n");
}
}
private int generateDecoderGroups(
final StringBuilder sb,
final String parentMessageClassName,
final List tokens,
int index,
final String indent)
throws IOException
{
for (int size = tokens.size(); index < size; index++)
{
final Token groupToken = tokens.get(index);
if (groupToken.signal() == Signal.BEGIN_GROUP)
{
final String groupName = decoderName(formatClassName(groupToken.name()));
sb.append(generateGroupDecoderProperty(groupName, groupToken, indent));
generateAnnotations(groupName, tokens, sb, index + 1, this::decoderName);
generateGroupDecoderClassHeader(sb, groupName, parentMessageClassName, tokens, index, indent + INDENT);
final List rootFields = new ArrayList<>();
index = collectRootFields(tokens, ++index, rootFields);
sb.append(generateDecoderFields(rootFields, indent + INDENT));
if (tokens.get(index).signal() == Signal.BEGIN_GROUP)
{
index = generateDecoderGroups(sb, parentMessageClassName, tokens, index, indent + INDENT);
}
sb.append(indent).append(" }\n");
}
}
return index;
}
private int generateEncoderGroups(
final StringBuilder sb,
final String parentMessageClassName,
final List tokens,
int index,
final String indent)
throws IOException
{
for (int size = tokens.size(); index < size; index++)
{
final Token groupToken = tokens.get(index);
if (groupToken.signal() == Signal.BEGIN_GROUP)
{
final String groupName = groupToken.name();
final String groupClassName = formatClassName(encoderName(groupName));
sb.append(generateGroupEncoderProperty(groupName, groupToken, indent));
generateAnnotations(groupClassName, tokens, sb, index + 1, this::encoderName);
generateGroupEncoderClassHeader(sb, groupName, parentMessageClassName, tokens, index, indent + INDENT);
final List rootFields = new ArrayList<>();
index = collectRootFields(tokens, ++index, rootFields);
sb.append(generateEncoderFields(groupClassName, rootFields, indent + INDENT));
if (tokens.get(index).signal() == Signal.BEGIN_GROUP)
{
index = generateEncoderGroups(sb, parentMessageClassName, tokens, index, indent + INDENT);
}
sb.append(indent).append(" }\n");
}
}
return index;
}
private void generateGroupDecoderClassHeader(
final StringBuilder sb,
final String groupName,
final String parentMessageClassName,
final List tokens,
final int index,
final String indent)
{
final String dimensionsClassName = formatClassName(tokens.get(index + 1).name());
final int dimensionHeaderLen = tokens.get(index + 1).encodedLength();
generateDecoderClassDeclaration(sb, groupName, parentMessageClassName, indent, dimensionsClassName, dimensionHeaderLen);
sb.append(String.format(
indent + " public void wrap(\n" +
indent + " final %s parentMessage, final %s buffer)\n" +
indent + " {\n" +
indent + " this.parentMessage = parentMessage;\n" +
indent + " this.buffer = buffer;\n" +
indent + " dimensions.wrap(buffer, parentMessage.limit());\n" +
indent + " blockLength = dimensions.blockLength();\n" +
indent + " count = dimensions.numInGroup();\n" +
indent + " index = -1;\n" +
indent + " parentMessage.limit(parentMessage.limit() + HEADER_SIZE);\n" +
indent + " }\n\n",
parentMessageClassName,
readOnlyBuffer
));
final int blockLength = tokens.get(index).encodedLength();
sb.append(indent).append(" public static int sbeHeaderSize()\n")
.append(indent).append(" {\n")
.append(indent).append(" return HEADER_SIZE;\n")
.append(indent).append(" }\n\n");
sb.append(String.format(
indent + " public static int sbeBlockLength()\n" +
indent + " {\n" +
indent + " return %d;\n" +
indent + " }\n\n",
blockLength
));
sb.append(String.format(
indent + " public int actingBlockLength()\n" +
indent + " {\n" +
indent + " return blockLength;\n" +
indent + " }\n\n" +
indent + " public int count()\n" +
indent + " {\n" +
indent + " return count;\n" +
indent + " }\n\n" +
indent + " @Override\n" +
indent + " public java.util.Iterator<%s> iterator()\n" +
indent + " {\n" +
indent + " return this;\n" +
indent + " }\n\n" +
indent + " @Override\n" +
indent + " public void remove()\n" +
indent + " {\n" +
indent + " throw new UnsupportedOperationException();\n" +
indent + " }\n\n" +
indent + " @Override\n" +
indent + " public boolean hasNext()\n" +
indent + " {\n" +
indent + " return (index + 1) < count;\n" +
indent + " }\n\n",
formatClassName(groupName)
));
sb.append(String.format(
indent + " @Override\n" +
indent + " public %s next()\n" +
indent + " {\n" +
indent + " if (index + 1 >= count)\n" +
indent + " {\n" +
indent + " throw new java.util.NoSuchElementException();\n" +
indent + " }\n\n" +
indent + " offset = parentMessage.limit();\n" +
indent + " parentMessage.limit(offset + blockLength);\n" +
indent + " ++index;\n\n" +
indent + " return this;\n" +
indent + " }\n",
formatClassName(groupName)
));
}
private void generateGroupEncoderClassHeader(
final StringBuilder sb,
final String groupName,
final String parentMessageClassName,
final List tokens,
final int index,
final String indent)
{
final String dimensionsClassName = formatClassName(encoderName(tokens.get(index + 1).name()));
final int dimensionHeaderSize = tokens.get(index + 1).encodedLength();
generateEncoderClassDeclaration(sb, groupName, parentMessageClassName, indent, dimensionsClassName, dimensionHeaderSize);
final int blockLength = tokens.get(index).encodedLength();
final String javaTypeForBlockLength = primitiveTypeName(tokens.get(index + 2));
final String javaTypeForNumInGroup = primitiveTypeName(tokens.get(index + 3));
sb.append(String.format(
indent + " public void wrap(final %1$s parentMessage, final %5$s buffer, final int count)\n" +
indent + " {\n" +
indent + " this.parentMessage = parentMessage;\n" +
indent + " this.buffer = buffer;\n" +
indent + " actingVersion = SCHEMA_VERSION;\n" +
indent + " dimensions.wrap(buffer, parentMessage.limit());\n" +
indent + " dimensions.blockLength((%2$s)%3$d);\n" +
indent + " dimensions.numInGroup((%4$s)count);\n" +
indent + " index = -1;\n" +
indent + " this.count = count;\n" +
indent + " blockLength = %3$d;\n" +
indent + " parentMessage.limit(parentMessage.limit() + HEADER_SIZE);\n" +
indent + " }\n\n",
parentMessageClassName,
javaTypeForBlockLength,
blockLength,
javaTypeForNumInGroup,
mutableBuffer
));
sb.append(indent).append(" public static int sbeHeaderSize()\n")
.append(indent).append(" {\n")
.append(indent).append(" return HEADER_SIZE;\n")
.append(indent).append(" }\n\n");
sb.append(String.format(
indent + " public static int sbeBlockLength()\n" +
indent + " {\n" +
indent + " return %d;\n" +
indent + " }\n\n",
blockLength
));
sb.append(String.format(
indent + " public %s next()\n" +
indent + " {\n" +
indent + " if (index + 1 >= count)\n" +
indent + " {\n" +
indent + " throw new java.util.NoSuchElementException();\n" +
indent + " }\n\n" +
indent + " offset = parentMessage.limit();\n" +
indent + " parentMessage.limit(offset + blockLength);\n" +
indent + " ++index;\n\n" +
indent + " return this;\n" +
indent + " }\n",
formatClassName(encoderName(groupName))
));
}
private String primitiveTypeName(final Token token)
{
return javaTypeName(token.encoding().primitiveType());
}
private void generateDecoderClassDeclaration(
final StringBuilder sb,
final String groupName,
final String parentMessageClassName,
final String indent,
final String dimensionsClassName,
final int dimensionHeaderSize)
{
sb.append(String.format(
"\n" +
indent + "public static class %1$s\n" +
indent + "implements Iterable<%1$s>, java.util.Iterator<%1$s>\n" +
indent + "{\n" +
indent + " private static final int HEADER_SIZE = %2$d;\n" +
indent + " private final %3$s dimensions = new %3$s();\n" +
indent + " private %4$s parentMessage;\n" +
indent + " private %5$s buffer;\n" +
indent + " private int blockLength;\n" +
indent + " private int actingVersion;\n" +
indent + " private int count;\n" +
indent + " private int index;\n" +
indent + " private int offset;\n\n",
formatClassName(groupName),
dimensionHeaderSize,
decoderName(dimensionsClassName),
parentMessageClassName,
readOnlyBuffer
));
}
private void generateEncoderClassDeclaration(
final StringBuilder sb,
final String groupName,
final String parentMessageClassName,
final String indent,
final String dimensionsClassName,
final int dimensionHeaderSize)
{
sb.append(String.format(
"\n" +
indent + "public static class %1$s\n" +
indent + "{\n" +
indent + " private static final int HEADER_SIZE = %2$d;\n" +
indent + " private final %3$s dimensions = new %3$s();\n" +
indent + " private %4$s parentMessage;\n" +
indent + " private %5$s buffer;\n" +
indent + " private int blockLength;\n" +
indent + " private int actingVersion;\n" +
indent + " private int count;\n" +
indent + " private int index;\n" +
indent + " private int offset;\n\n",
formatClassName(encoderName(groupName)),
dimensionHeaderSize,
dimensionsClassName,
parentMessageClassName,
mutableBuffer
));
}
private CharSequence generateGroupDecoderProperty(final String groupName, final Token token, final String indent)
{
final StringBuilder sb = new StringBuilder();
final String className = formatClassName(groupName);
final String propertyName = formatPropertyName(token.name());
sb.append(String.format(
"\n" +
indent + " private final %s %s = new %s();\n",
className,
propertyName,
className
));
sb.append(String.format(
"\n" +
indent + " public static long %sId()\n" +
indent + " {\n" +
indent + " return %d;\n" +
indent + " }\n",
formatPropertyName(groupName),
token.id()
));
sb.append(String.format(
"\n" +
indent + " public %1$s %2$s()\n" +
indent + " {\n" +
indent + " %2$s.wrap(parentMessage, buffer);\n" +
indent + " return %2$s;\n" +
indent + " }\n",
className,
propertyName
));
return sb;
}
private CharSequence generateGroupEncoderProperty(final String groupName, final Token token, final String indent)
{
final StringBuilder sb = new StringBuilder();
final String className = formatClassName(encoderName(groupName));
final String propertyName = formatPropertyName(groupName);
sb.append(String.format(
"\n" +
indent + " private final %s %s = new %s();\n",
className,
propertyName,
className
));
sb.append(String.format(
"\n" +
indent + " public static long %sId()\n" +
indent + " {\n" +
indent + " return %d;\n" +
indent + " }\n",
formatPropertyName(groupName),
token.id()
));
sb.append(String.format(
"\n" +
indent + " public %1$s %2$sCount(final int count)\n" +
indent + " {\n" +
indent + " %2$s.wrap(parentMessage, buffer, count);\n" +
indent + " return %2$s;\n" +
indent + " }\n",
className,
propertyName
));
return sb;
}
private CharSequence generateVarDataDecoders(final List tokens)
{
final StringBuilder sb = new StringBuilder();
for (int i = 0, size = tokens.size(); i < size; i++)
{
final Token token = tokens.get(i);
if (token.signal() != Signal.BEGIN_VAR_DATA)
{
continue;
}
generateFieldIdMethod(sb, token, BASE_INDENT);
final String characterEncoding = tokens.get(i + 3).encoding().characterEncoding();
generateCharacterEncodingMethod(sb, token.name(), characterEncoding, BASE_INDENT);
generateFieldMetaAttributeMethod(sb, token, BASE_INDENT);
final String propertyName = toUpperFirstChar(token.name());
final Token lengthToken = tokens.get(i + 2);
final int sizeOfLengthField = lengthToken.encodedLength();
final Encoding lengthEncoding = lengthToken.encoding();
final String lengthTypePrefix = lengthEncoding.primitiveType().primitiveName();
final String byteOrderStr = byteOrderString(lengthEncoding);
sb.append(String.format(
"\n" +
" public static int %sHeaderLength()\n" +
" {\n" +
" return %d;\n" +
" }\n",
toLowerFirstChar(propertyName),
sizeOfLengthField
));
sb.append(String.format(
"\n" +
" public int %sLength()\n" +
" {\n" +
"%s" +
" final int sizeOfLengthField = %d;\n" +
" final int limit = limit();\n" +
" buffer.checkLimit(limit + sizeOfLengthField);\n\n" +
" return CodecUtil.%sGet(buffer, limit%s);\n" +
" }\n",
toLowerFirstChar(propertyName),
generateArrayFieldNotPresentCondition(token.version(), BASE_INDENT),
sizeOfLengthField,
lengthTypePrefix,
byteOrderStr
));
generateVarDataDecodeMethods(
sb, token, propertyName, sizeOfLengthField, lengthTypePrefix, byteOrderStr, characterEncoding);
}
return sb;
}
private CharSequence generateVarDataEncoders(final String className, final List tokens)
{
final StringBuilder sb = new StringBuilder();
for (int i = 0, size = tokens.size(); i < size; i++)
{
final Token token = tokens.get(i);
if (token.signal() != Signal.BEGIN_VAR_DATA)
{
continue;
}
generateFieldIdMethod(sb, token, BASE_INDENT);
final String characterEncoding = tokens.get(i + 3).encoding().characterEncoding();
generateCharacterEncodingMethod(sb, token.name(), characterEncoding, BASE_INDENT);
generateFieldMetaAttributeMethod(sb, token, BASE_INDENT);
final String propertyName = toUpperFirstChar(token.name());
final Token lengthToken = tokens.get(i + 2);
final int sizeOfLengthField = lengthToken.encodedLength();
final Encoding lengthEncoding = lengthToken.encoding();
final String lengthJavaType = javaTypeName(lengthEncoding.primitiveType());
final String lengthTypePrefix = lengthEncoding.primitiveType().primitiveName();
final String byteOrderStr = byteOrderString(lengthEncoding);
generateVarDataEncodeMethods(
sb,
propertyName,
sizeOfLengthField,
lengthJavaType,
lengthTypePrefix,
byteOrderStr,
characterEncoding,
className);
}
return sb;
}
private void generateVarDataDecodeMethods(
final StringBuilder sb,
final Token token,
final String propertyName,
final int sizeOfLengthField,
final String lengthTypePrefix,
final String byteOrderStr,
final String characterEncoding)
{
generateVarDataTypedDecoder(
sb,
token,
propertyName,
sizeOfLengthField,
fullMutableBuffer,
lengthTypePrefix,
byteOrderStr);
generateVarDataTypedDecoder(
sb,
token,
propertyName,
sizeOfLengthField,
"byte[]",
lengthTypePrefix,
byteOrderStr);
sb.append(String.format(
"\n" +
" public String %1$s()\n" +
" {\n" +
"%2$s" +
" final int sizeOfLengthField = %3$d;\n" +
" final int limit = limit();\n" +
" buffer.checkLimit(limit + sizeOfLengthField);\n" +
" final int dataLength = CodecUtil.%4$sGet(buffer, limit%5$s);\n" +
" limit(limit + sizeOfLengthField + dataLength);\n" +
" final byte[] tmp = new byte[dataLength];\n" +
" buffer.getBytes(limit + sizeOfLengthField, tmp, 0, dataLength);\n\n" +
" final String value;\n" +
" try\n" +
" {\n" +
" value = new String(tmp, \"%6$s\");\n" +
" }\n" +
" catch (final java.io.UnsupportedEncodingException ex)\n" +
" {\n" +
" throw new RuntimeException(ex);\n" +
" }\n\n" +
" return value;\n" +
" }\n",
toLowerFirstChar(propertyName),
generateStringNotPresentCondition(token.version(), BASE_INDENT),
sizeOfLengthField,
lengthTypePrefix,
byteOrderStr,
characterEncoding
));
}
private void generateVarDataEncodeMethods(
final StringBuilder sb,
final String propertyName,
final int sizeOfLengthField,
final String lengthJavaType,
final String lengthTypePrefix,
final String byteOrderStr,
final String characterEncoding,
final String className)
{
generateVarDataTypedEncoder(
sb,
propertyName,
sizeOfLengthField,
fullReadOnlyBuffer,
lengthJavaType,
lengthTypePrefix,
byteOrderStr);
generateVarDataTypedEncoder(
sb,
propertyName,
sizeOfLengthField,
"byte[]",
lengthJavaType,
lengthTypePrefix,
byteOrderStr);
sb.append(String.format(
"\n" +
" public %7$s %1$s(final String value)\n" +
" {\n" +
" final byte[] bytes;\n" +
" try\n" +
" {\n" +
" bytes = value.getBytes(\"%2$s\");\n" +
" }\n" +
" catch (final java.io.UnsupportedEncodingException ex)\n" +
" {\n" +
" throw new RuntimeException(ex);\n" +
" }\n\n" +
" final int length = bytes.length;\n" +
" final int sizeOfLengthField = %3$d;\n" +
" final int limit = limit();\n" +
" limit(limit + sizeOfLengthField + length);\n" +
" CodecUtil.%4$sPut(buffer, limit, (%5$s)length%6$s);\n" +
" buffer.putBytes(limit + sizeOfLengthField, bytes, 0, length);\n\n" +
" return this;\n" +
" }\n",
toLowerFirstChar(propertyName),
characterEncoding,
sizeOfLengthField,
lengthTypePrefix,
lengthJavaType,
byteOrderStr,
className
));
}
private void generateVarDataTypedDecoder(
final StringBuilder sb,
final Token token,
final String propertyName,
final int sizeOfLengthField,
final String exchangeType,
final String lengthTypePrefix,
final String byteOrderStr)
{
sb.append(String.format(
"\n" +
" public int get%s(final %s dst, final int dstOffset, final int length)\n" +
" {\n" +
"%s" +
" final int sizeOfLengthField = %d;\n" +
" final int limit = limit();\n" +
" buffer.checkLimit(limit + sizeOfLengthField);\n" +
" final int dataLength = CodecUtil.%sGet(buffer, limit%s);\n" +
" final int bytesCopied = Math.min(length, dataLength);\n" +
" limit(limit + sizeOfLengthField + dataLength);\n" +
" buffer.getBytes(limit + sizeOfLengthField, dst, dstOffset, bytesCopied);\n\n" +
" return bytesCopied;\n" +
" }\n",
propertyName,
exchangeType,
generateArrayFieldNotPresentCondition(token.version(), BASE_INDENT),
sizeOfLengthField,
lengthTypePrefix,
byteOrderStr
));
}
private void generateVarDataTypedEncoder(
final StringBuilder sb,
final String propertyName,
final int sizeOfLengthField,
final String exchangeType,
final String lengthJavaType,
final String lengthTypePrefix,
final String byteOrderStr)
{
sb.append(String.format(
"\n" +
" public int put%s(final %s src, final int srcOffset, final int length)\n" +
" {\n" +
" final int sizeOfLengthField = %d;\n" +
" final int limit = limit();\n" +
" limit(limit + sizeOfLengthField + length);\n" +
" CodecUtil.%sPut(buffer, limit, (%s)length%s);\n" +
" buffer.putBytes(limit + sizeOfLengthField, src, srcOffset, length);\n\n" +
" return length;\n" +
" }\n",
propertyName,
exchangeType,
sizeOfLengthField,
lengthTypePrefix,
lengthJavaType,
byteOrderStr
));
}
private void generateBitSet(final List tokens) throws IOException
{
final Token token = tokens.get(0);
final String bitSetName = formatClassName(token.name());
final String decoderName = decoderName(bitSetName);
final String encoderName = encoderName(bitSetName);
final List messageBody = getMessageBody(tokens);
try (final Writer out = outputManager.createOutput(decoderName))
{
generateFixedFlyweightHeader(token, decoderName, out, readOnlyBuffer, fullReadOnlyBuffer);
out.append(generateChoiceDecoders(messageBody));
out.append("}\n");
}
try (final Writer out = outputManager.createOutput(encoderName))
{
generateFixedFlyweightHeader(token, encoderName, out, mutableBuffer, fullMutableBuffer);
out.append(generateChoiceClear(encoderName, token));
out.append(generateChoiceEncoders(encoderName, messageBody));
out.append("}\n");
}
}
private void generateFixedFlyweightHeader(
final Token token, final String encoderName, final Writer out, final String buffer, final String fullBuffer)
throws IOException
{
out.append(generateFileHeader(ir.applicableNamespace(), fullBuffer));
out.append(generateClassDeclaration(encoderName));
out.append(generateFixedFlyweightCode(encoderName, token.encodedLength(), false, buffer));
}
private void generateEnum(final List tokens) throws IOException
{
final String enumName = formatClassName(tokens.get(0).name());
try (final Writer out = outputManager.createOutput(enumName))
{
out.append(generateEnumFileHeader(ir.applicableNamespace()));
out.append(generateEnumDeclaration(enumName));
out.append(generateEnumValues(getMessageBody(tokens)));
out.append(generateEnumBody(tokens.get(0), enumName));
out.append(generateEnumLookupMethod(getMessageBody(tokens), enumName));
out.append("}\n");
}
}
private void generateComposite(final List tokens) throws IOException
{
final Token token = tokens.get(0);
final String compositeName = formatClassName(token.name());
final String decoderName = decoderName(compositeName);
final String encoderName = encoderName(compositeName);
final List messageBody = getMessageBody(tokens);
try (final Writer out = outputManager.createOutput(decoderName))
{
generateFixedFlyweightHeader(token, decoderName, out, readOnlyBuffer, fullReadOnlyBuffer);
out.append(concatEncodingTokens(messageBody,
(tok) -> generatePrimitiveDecoder(tok.name(), tok, BASE_INDENT)));
out.append("}\n");
}
try (final Writer out = outputManager.createOutput(encoderName))
{
generateFixedFlyweightHeader(token, encoderName, out, mutableBuffer, fullMutableBuffer);
out.append(concatEncodingTokens(messageBody,
(tok) -> generatePrimitiveEncoder(encoderName, tok.name(), tok, BASE_INDENT)));
out.append("}\n");
}
}
private CharSequence generateChoiceClear(final String bitSetClassName, final Token token)
{
final StringBuilder sb = new StringBuilder();
final Encoding encoding = token.encoding();
final String typePrefix = encoding.primitiveType().primitiveName();
final String literalValue = generateLiteral(encoding.primitiveType(), "0");
final String byteOrderStr = byteOrderString(encoding);
sb.append(String.format(
"\n" +
" public %s clear()\n" +
" {\n" +
" CodecUtil.%sPut(buffer, offset, %s%s);\n" +
" return this;\n" +
" }\n",
bitSetClassName,
typePrefix,
literalValue,
byteOrderStr
));
return sb;
}
private CharSequence generateChoiceDecoders(final List tokens)
{
return concatTokens(tokens, Signal.CHOICE,
(token) ->
{
final String choiceName = token.name();
final Encoding encoding = token.encoding();
final String typePrefix = encoding.primitiveType().primitiveName();
final String choiceBitPosition = encoding.constValue().toString();
final String byteOrderStr = byteOrderString(encoding);
return String.format(
"\n" +
" public boolean %s()\n" +
" {\n" +
" return CodecUtil.%sGetChoice(buffer, offset, %s%s);\n" +
" }\n\n",
choiceName,
typePrefix,
choiceBitPosition,
byteOrderStr
);
});
}
private CharSequence generateChoiceEncoders(final String bitSetClassName, final List tokens)
{
return concatTokens(tokens, Signal.CHOICE, (token) ->
{
final String choiceName = token.name();
final Encoding encoding = token.encoding();
final String typePrefix = encoding.primitiveType().primitiveName();
final String choiceBitPosition = encoding.constValue().toString();
final String byteOrderStr = byteOrderString(encoding);
return String.format(
"\n" +
" public %s %s(final boolean value)\n" +
" {\n" +
" CodecUtil.%sPutChoice(buffer, offset, %s, value%s);\n" +
" return this;\n" +
" }\n",
bitSetClassName,
choiceName,
typePrefix,
choiceBitPosition,
byteOrderStr
);
});
}
private CharSequence generateEnumValues(final List tokens)
{
final StringBuilder sb = new StringBuilder();
for (final Token token : tokens)
{
final Encoding encoding = token.encoding();
final CharSequence constVal = generateLiteral(encoding.primitiveType(), encoding.constValue().toString());
sb.append(" ").append(token.name()).append('(').append(constVal).append("),\n");
}
final Token token = tokens.get(0);
final Encoding encoding = token.encoding();
final CharSequence nullVal = generateLiteral(encoding.primitiveType(), encoding.applicableNullValue().toString());
sb.append(" ").append("NULL_VAL").append('(').append(nullVal).append(')');
sb.append(";\n\n");
return sb;
}
private CharSequence generateEnumBody(final Token token, final String enumName)
{
final String javaEncodingType = primitiveTypeName(token);
return String.format(
" private final %1$s value;\n\n" +
" %2$s(final %1$s value)\n" +
" {\n" +
" this.value = value;\n" +
" }\n\n" +
" public %1$s value()\n" +
" {\n" +
" return value;\n" +
" }\n\n",
javaEncodingType,
enumName
);
}
private CharSequence generateEnumLookupMethod(final List tokens, final String enumName)
{
final StringBuilder sb = new StringBuilder();
final PrimitiveType primitiveType = tokens.get(0).encoding().primitiveType();
sb.append(String.format(
" public static %s get(final %s value)\n" +
" {\n" +
" switch (value)\n" +
" {\n",
enumName,
javaTypeName(primitiveType)
));
for (final Token token : tokens)
{
sb.append(String.format(
" case %s: return %s;\n",
token.encoding().constValue().toString(),
token.name())
);
}
sb.append(String.format(
" }\n\n" +
" if (%s == value)\n" +
" {\n" +
" return NULL_VAL;\n" +
" }\n\n" +
" throw new IllegalArgumentException(\"Unknown value: \" + value);\n" +
" }\n",
generateLiteral(primitiveType, tokens.get(0).encoding().applicableNullValue().toString())
));
return sb;
}
private CharSequence generateFileHeader(final String packageName, final String fqBuffer)
{
return String.format(
"/* Generated SBE (Simple Binary Encoding) message codec */\n" +
"package %s;\n\n" +
"import uk.co.real_logic.sbe.codec.java.*;\n" +
"import %s;\n\n",
packageName,
fqBuffer
);
}
private CharSequence generateEnumFileHeader(final String packageName)
{
return String.format(
"/* Generated SBE (Simple Binary Encoding) message codec */\n" +
"package %s;\n\n",
packageName
);
}
private void generateAnnotations(
final String className,
final List tokens,
final Appendable out,
int index,
final Function nameMapping)
throws IOException
{
final List groupClassNames = new ArrayList<>();
int level = 0;
for (int size = tokens.size(); index < size; index++)
{
if (tokens.get(index).signal() == Signal.BEGIN_GROUP)
{
if (++level == 1)
{
final Token groupToken = tokens.get(index);
final String groupName = groupToken.name();
groupClassNames.add(formatClassName(nameMapping.apply(groupName)));
}
}
else if (tokens.get(index).signal() == Signal.END_GROUP)
{
if (--level < 0)
{
break;
}
}
}
if (groupClassNames.isEmpty())
{
return;
}
out.append("@GroupOrder({");
index = 0;
for (final String name : groupClassNames)
{
out.append(className).append('.').append(name).append(".class");
if (++index < groupClassNames.size())
{
out.append(", ");
}
}
out.append("})\n");
}
private CharSequence generateClassDeclaration(final String className)
{
return String.format(
"@SuppressWarnings(\"all\")\n" +
"public class %s\n" +
"{\n",
className
);
}
private void generateMetaAttributeEnum() throws IOException
{
try (final Writer out = outputManager.createOutput(META_ATTRIBUTE_ENUM))
{
out.append(String.format(
"/* Generated SBE (Simple Binary Encoding) message codec */\n" +
"package %s;\n\n" +
"public enum MetaAttribute\n" +
"{\n" +
" EPOCH,\n" +
" TIME_UNIT,\n" +
" SEMANTIC_TYPE\n" +
"}\n",
ir.applicableNamespace()
));
}
}
private CharSequence generateEnumDeclaration(final String name)
{
return "public enum " + name + "\n{\n";
}
private CharSequence generatePrimitiveDecoder(
final String propertyName, final Token token, final String indent)
{
final StringBuilder sb = new StringBuilder();
sb.append(generatePrimitiveFieldMetaData(propertyName, token, indent));
if (token.isConstantEncoding())
{
sb.append(generateConstPropertyMethods(propertyName, token, indent));
}
else
{
sb.append(generatePrimitivePropertyDecodeMethods(propertyName, token, indent));
}
return sb;
}
private CharSequence generatePrimitiveEncoder(
final String containingClassName, final String propertyName, final Token token, final String indent)
{
final StringBuilder sb = new StringBuilder();
sb.append(generatePrimitiveFieldMetaData(propertyName, token, indent));
if (token.isConstantEncoding())
{
sb.append(generateConstPropertyMethods(propertyName, token, indent));
}
else
{
sb.append(generatePrimitivePropertyEncodeMethods(containingClassName, propertyName, token, indent));
}
return sb;
}
private CharSequence generatePrimitivePropertyDecodeMethods(
final String propertyName, final Token token, final String indent)
{
return token.switchArray(
() -> generateSingleValuePropertyDecode(propertyName, token, indent),
() -> generateArrayPropertyDecode(propertyName, token, indent));
}
private CharSequence generatePrimitivePropertyEncodeMethods(
final String containingClassName, final String propertyName, final Token token, final String indent)
{
return token.switchArray(
() -> generateSingleValuePropertyEncode(containingClassName, propertyName, token, indent),
() -> generateArrayPropertyEncode(containingClassName, propertyName, token, indent));
}
private CharSequence generatePrimitiveFieldMetaData(final String propertyName, final Token token, final String indent)
{
final StringBuilder sb = new StringBuilder();
final PrimitiveType primitiveType = token.encoding().primitiveType();
final String javaTypeName = javaTypeName(primitiveType);
sb.append(String.format(
"\n" +
indent + " public static %s %sNullValue()\n" +
indent + " {\n" +
indent + " return %s;\n" +
indent + " }\n",
javaTypeName,
propertyName,
generateLiteral(primitiveType, token.encoding().applicableNullValue().toString())
));
sb.append(String.format(
"\n" +
indent + " public static %s %sMinValue()\n" +
indent + " {\n" +
indent + " return %s;\n" +
indent + " }\n",
javaTypeName,
propertyName,
generateLiteral(primitiveType, token.encoding().applicableMinValue().toString())
));
sb.append(String.format(
"\n" +
indent + " public static %s %sMaxValue()\n" +
indent + " {\n" +
indent + " return %s;\n" +
indent + " }\n",
javaTypeName,
propertyName,
generateLiteral(primitiveType, token.encoding().applicableMaxValue().toString())
));
return sb;
}
private CharSequence generateSingleValuePropertyDecode(
final String propertyName, final Token token, final String indent)
{
final Encoding encoding = token.encoding();
final String javaTypeName = javaTypeName(encoding.primitiveType());
final String typePrefix = encoding.primitiveType().primitiveName();
final int offset = token.offset();
final String byteOrderStr = byteOrderString(encoding);
return String.format(
"\n" +
indent + " public %s %s()\n" +
indent + " {\n" +
"%s" +
indent + " return CodecUtil.%sGet(buffer, offset + %d%s);\n" +
indent + " }\n\n",
javaTypeName,
propertyName,
generateFieldNotPresentCondition(token.version(), encoding, indent),
typePrefix,
offset,
byteOrderStr
);
}
private CharSequence generateSingleValuePropertyEncode(
final String containingClassName, final String propertyName, final Token token, final String indent)
{
final Encoding encoding = token.encoding();
final String javaTypeName = javaTypeName(encoding.primitiveType());
final String typePrefix = encoding.primitiveType().primitiveName();
final int offset = token.offset();
final String byteOrderStr = byteOrderString(encoding);
return String.format(
indent + " public %s %s(final %s value)\n" +
indent + " {\n" +
indent + " CodecUtil.%sPut(buffer, offset + %d, value%s);\n" +
indent + " return this;\n" +
indent + " }\n",
formatClassName(containingClassName),
propertyName,
javaTypeName,
typePrefix,
offset,
byteOrderStr
);
}
private CharSequence generateFieldNotPresentCondition(final int sinceVersion, final Encoding encoding, final String indent)
{
if (0 == sinceVersion)
{
return "";
}
return String.format(
indent + " if (actingVersion < %d)\n" +
indent + " {\n" +
indent + " return %s;\n" +
indent + " }\n\n",
sinceVersion,
generateLiteral(encoding.primitiveType(), encoding.applicableNullValue().toString())
);
}
private CharSequence generateArrayFieldNotPresentCondition(final int sinceVersion, final String indent)
{
if (0 == sinceVersion)
{
return "";
}
return String.format(
indent + " if (actingVersion < %d)\n" +
indent + " {\n" +
indent + " return 0;\n" +
indent + " }\n\n",
sinceVersion
);
}
private CharSequence generateStringNotPresentCondition(final int sinceVersion, final String indent)
{
if (0 == sinceVersion)
{
return "";
}
return String.format(
indent + " if (actingVersion < %d)\n" +
indent + " {\n" +
indent + " return \"\";\n" +
indent + " }\n\n",
sinceVersion
);
}
private CharSequence generateTypeFieldNotPresentCondition(final int sinceVersion, final String indent)
{
if (0 == sinceVersion)
{
return "";
}
return String.format(
indent + " if (actingVersion < %d)\n" +
indent + " {\n" +
indent + " return null;\n" +
indent + " }\n\n",
sinceVersion
);
}
private CharSequence generateArrayPropertyDecode(final String propertyName, final Token token, final String indent)
{
final Encoding encoding = token.encoding();
final String javaTypeName = javaTypeName(encoding.primitiveType());
final String typePrefix = encoding.primitiveType().primitiveName();
final int offset = token.offset();
final String byteOrderStr = byteOrderString(encoding);
final int fieldLength = token.arrayLength();
final int typeSize = sizeOfPrimitive(encoding);
final StringBuilder sb = new StringBuilder();
generateArrayLengthMethod(propertyName, indent, fieldLength, sb);
sb.append(String.format(
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 + " return CodecUtil.%sGet(buffer, this.offset + %d + (index * %d)%s);\n" +
indent + " }\n\n",
javaTypeName,
propertyName,
fieldLength,
generateFieldNotPresentCondition(token.version(), encoding, indent),
typePrefix,
offset,
typeSize,
byteOrderStr
));
if (encoding.primitiveType() == PrimitiveType.CHAR)
{
generateCharacterEncodingMethod(sb, propertyName, encoding.characterEncoding(), indent);
sb.append(String.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(" +
indent + "\"dstOffset out of range for copy: offset=\" + dstOffset);\n" +
indent + " }\n\n" +
"%s" +
indent + " CodecUtil.charsGet(buffer, this.offset + %d, dst, dstOffset, length);\n" +
indent + " return length;\n" +
indent + " }\n\n",
toUpperFirstChar(propertyName),
fieldLength,
generateArrayFieldNotPresentCondition(token.version(), indent),
offset
));
}
return sb;
}
private void generateArrayLengthMethod(
final String propertyName, final String indent, final int fieldLength, final StringBuilder sb)
{
sb.append(String.format(
"\n" +
indent + " public static int %sLength()\n" +
indent + " {\n" +
indent + " return %d;\n" +
indent + " }\n\n",
propertyName,
fieldLength
));
}
private String byteOrderString(final Encoding encoding)
{
final ByteOrder byteOrder = encoding.byteOrder();
return sizeOfPrimitive(encoding) == 1 ? "" : ", java.nio.ByteOrder." + byteOrder;
}
private CharSequence generateArrayPropertyEncode(
final String containingClassName, final String propertyName, final Token token, final String indent)
{
final Encoding encoding = token.encoding();
final String javaTypeName = javaTypeName(encoding.primitiveType());
final String typePrefix = encoding.primitiveType().primitiveName();
final int offset = token.offset();
final String byteOrderStr = byteOrderString(encoding);
final int fieldLength = token.arrayLength();
final int typeSize = sizeOfPrimitive(encoding);
final StringBuilder sb = new StringBuilder();
generateArrayLengthMethod(propertyName, indent, fieldLength, sb);
sb.append(String.format(
indent + " public void %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 + " CodecUtil.%sPut(buffer, this.offset + %d + (index * %d), value%s);\n" +
indent + " }\n",
propertyName,
javaTypeName,
fieldLength,
typePrefix,
offset,
typeSize,
byteOrderStr
));
if (encoding.primitiveType() == PrimitiveType.CHAR)
{
generateCharacterEncodingMethod(sb, propertyName, encoding.characterEncoding(), indent);
sb.append(String.format(
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(" +
indent + "\"srcOffset out of range for copy: offset=\" + srcOffset);\n" +
indent + " }\n\n" +
indent + " CodecUtil.charsPut(buffer, this.offset + %d, src, srcOffset, length);\n" +
indent + " return this;\n" +
indent + " }\n",
formatClassName(containingClassName),
toUpperFirstChar(propertyName),
fieldLength,
offset
));
}
return sb;
}
private int sizeOfPrimitive(final Encoding encoding)
{
return encoding.primitiveType().size();
}
private void generateCharacterEncodingMethod(
final StringBuilder sb, final String propertyName, final String encoding, final String indent)
{
sb.append(String.format(
"\n" +
indent + " public static String %sCharacterEncoding()\n" +
indent + " {\n" +
indent + " return \"%s\";\n" +
indent + " }\n",
formatPropertyName(propertyName),
encoding
));
}
private CharSequence generateConstPropertyMethods(final String propertyName, final Token token, final String indent)
{
final Encoding encoding = token.encoding();
if (encoding.primitiveType() != PrimitiveType.CHAR)
{
return String.format(
"\n" +
indent + " public %s %s()\n" +
indent + " {\n" +
indent + " return %s;\n" +
indent + " }\n",
javaTypeName(encoding.primitiveType()),
propertyName,
generateLiteral(encoding.primitiveType(), encoding.constValue().toString())
);
}
final StringBuilder sb = new StringBuilder();
final String javaTypeName = javaTypeName(encoding.primitiveType());
final byte[] constantValue = encoding.constValue().byteArrayValue(encoding.primitiveType());
final CharSequence values = generateByteLiteralList(encoding.constValue().byteArrayValue(encoding.primitiveType()));
sb.append(String.format(
"\n" +
indent + " private static final byte[] %s_VALUE = {%s};\n",
propertyName.toUpperCase(),
values
));
generateArrayLengthMethod(propertyName, indent, constantValue.length, sb);
sb.append(String.format(
indent + " public %s %s(final int index)\n" +
indent + " {\n" +
indent + " return %s_VALUE[index];\n" +
indent + " }\n\n",
javaTypeName,
propertyName,
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" +
indent + " return bytesCopied;\n" +
indent + " }\n",
toUpperFirstChar(propertyName),
constantValue.length,
propertyName.toUpperCase()
));
return sb;
}
private 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 boolean callsSuper,
final String bufferImplementation)
{
final String body = callsSuper ? " super.wrap(buffer, offset);\n" : "";
return String.format(
" public static final int ENCODED_LENGTH = %2$d;\n" +
" private %3$s buffer;\n" +
" private int offset;\n" +
" public %1$s wrap(final %3$s buffer, final int offset)\n" +
" {\n" +
" this.buffer = buffer;\n" +
"%4$s" +
" this.offset = offset;\n" +
" return this;\n" +
" }\n\n" +
" public int encodedLength()\n" +
" {\n" +
" return ENCODED_LENGTH;\n" +
" }\n",
className,
size,
bufferImplementation,
body
);
}
private CharSequence generateDecoderFlyweightCode(final String className, final Token token)
{
final String wrapMethod = String.format(
" public %1$s wrap(\n" +
" final %2$s buffer, final int offset, final int actingBlockLength, final int actingVersion)\n" +
" {\n" +
" this.buffer = buffer;\n" +
" this.offset = offset;\n" +
" this.actingBlockLength = actingBlockLength;\n" +
" this.actingVersion = actingVersion;\n" +
" limit(offset + actingBlockLength);\n\n" +
" return this;\n" +
" }\n\n",
className,
readOnlyBuffer);
return generateFlyweightCode(className, token, wrapMethod, readOnlyBuffer);
}
private CharSequence generateFlyweightCode(
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 templateIdType = javaTypeName(headerStructure.templateIdType());
final String schemaIdType = javaTypeName(headerStructure.schemaIdType());
final String schemaVersionType = javaTypeName(headerStructure.schemaVersionType());
final String semanticType = token.encoding().semanticType() == null ? "" : token.encoding().semanticType();
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\n" +
" private final %9$s parentMessage = this;\n" +
" private %11$s buffer;\n" +
" protected int offset;\n" +
" protected int limit;\n" +
" protected int actingBlockLength;\n" +
" protected int actingVersion;\n" +
"\n" +
" public %1$s sbeBlockLength()\n" +
" {\n" +
" return BLOCK_LENGTH;\n" +
" }\n\n" +
" public %3$s sbeTemplateId()\n" +
" {\n" +
" return TEMPLATE_ID;\n" +
" }\n\n" +
" public %5$s sbeSchemaId()\n" +
" {\n" +
" return SCHEMA_ID;\n" +
" }\n\n" +
" public %7$s sbeSchemaVersion()\n" +
" {\n" +
" return SCHEMA_VERSION;\n" +
" }\n\n" +
" public String sbeSemanticType()\n" +
" {\n" +
" return \"%10$s\";\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" +
" buffer.checkLimit(limit);\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(token.version())),
className,
semanticType,
bufferImplementation,
wrapMethod
);
}
private CharSequence generateEncoderFlyweightCode(final String className, final Token token)
{
final String wrapMethod = String.format(
" public %1$s wrap(final %2$s buffer, final int offset)\n" +
" {\n" +
" this.buffer = buffer;\n" +
" this.offset = offset;\n" +
" limit(offset + BLOCK_LENGTH);\n" +
" return this;\n" +
" }\n\n",
className,
mutableBuffer);
return generateFlyweightCode(className, token, wrapMethod, mutableBuffer);
}
private CharSequence generateEncoderFields(final String containingClassName, final List tokens, final String indent)
{
final StringBuilder sb = new StringBuilder();
eachField(tokens, (signalToken, encodingToken) ->
{
final String propertyName = formatPropertyName(signalToken.name());
final String typeName = formatClassName(encoderName(encodingToken.name()));
switch (encodingToken.signal())
{
case ENCODING:
sb.append(generatePrimitiveEncoder(containingClassName, propertyName, encodingToken, indent));
break;
case BEGIN_ENUM:
sb.append(generateEnumEncoder(containingClassName, propertyName, encodingToken, indent));
break;
case BEGIN_SET:
sb.append(generateBitSetProperty(propertyName, encodingToken, indent, typeName));
break;
case BEGIN_COMPOSITE:
sb.append(generateCompositeProperty(propertyName, encodingToken, indent, typeName));
break;
}
});
return sb;
}
private CharSequence generateDecoderFields(
final List tokens,
final String indent)
{
final StringBuilder sb = new StringBuilder();
eachField(tokens,
(signalToken, encodingToken) ->
{
final String propertyName = formatPropertyName(signalToken.name());
final String typeName = decoderName(formatClassName(encodingToken.name()));
generateFieldIdMethod(sb, signalToken, indent);
generateFieldMetaAttributeMethod(sb, signalToken, indent);
switch (encodingToken.signal())
{
case ENCODING:
sb.append(generatePrimitiveDecoder(propertyName, encodingToken, indent));
break;
case BEGIN_ENUM:
sb.append(generateEnumDecoder(propertyName, encodingToken, indent));
break;
case BEGIN_SET:
sb.append(generateBitSetProperty(propertyName, encodingToken, indent, typeName));
break;
case BEGIN_COMPOSITE:
sb.append(generateCompositeProperty(propertyName, encodingToken, indent, typeName));
break;
}
});
return sb;
}
private void eachField(final List tokens, final BiConsumer consumer)
{
for (int i = 0, size = tokens.size(); i < size; i++)
{
final Token signalToken = tokens.get(i);
if (signalToken.signal() == Signal.BEGIN_FIELD)
{
final Token encodingToken = tokens.get(i + 1);
consumer.accept(signalToken, encodingToken);
}
}
}
private void generateFieldIdMethod(final StringBuilder sb, final Token token, final String indent)
{
sb.append(String.format(
"\n" +
indent + " public static int %sId()\n" +
indent + " {\n" +
indent + " return %d;\n" +
indent + " }\n",
token.name(),
token.id()
));
}
private 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();
sb.append(String.format(
"\n" +
indent + " public static String %sMetaAttribute(final MetaAttribute metaAttribute)\n" +
indent + " {\n" +
indent + " switch (metaAttribute)\n" +
indent + " {\n" +
indent + " case EPOCH: return \"%s\";\n" +
indent + " case TIME_UNIT: return \"%s\";\n" +
indent + " case SEMANTIC_TYPE: return \"%s\";\n" +
indent + " }\n\n" +
indent + " return \"\";\n" +
indent + " }\n",
token.name(),
epoch,
timeUnit,
semanticType
));
}
private CharSequence generateEnumDecoder(final String propertyName, final Token token, final String indent)
{
final String enumName = formatClassName(token.name());
final Encoding encoding = token.encoding();
final String typePrefix = encoding.primitiveType().primitiveName();
final int offset = token.offset();
final String byteOrderStr = byteOrderString(encoding);
return String.format(
"\n" +
indent + " public %s %s()\n" +
indent + " {\n" +
"%s" +
indent + " return %s.get(CodecUtil.%sGet(buffer, offset + %d%s));\n" +
indent + " }\n\n",
enumName,
propertyName,
generateTypeFieldNotPresentCondition(token.version(), indent),
enumName,
typePrefix,
offset,
byteOrderStr
);
}
private CharSequence generateEnumEncoder(
final String containingClassName, final String propertyName, final Token token, final String indent)
{
final String enumName = formatClassName(token.name());
final Encoding encoding = token.encoding();
final String typePrefix = encoding.primitiveType().primitiveName();
final int offset = token.offset();
final String byteOrderStr = byteOrderString(encoding);
return String.format(
indent + " public %s %s(final %s value)\n" +
indent + " {\n" +
indent + " CodecUtil.%sPut(buffer, offset + %d, value.value()%s);\n" +
indent + " return this;\n" +
indent + " }\n",
formatClassName(containingClassName),
propertyName,
enumName,
typePrefix,
offset,
byteOrderStr
);
}
private Object generateBitSetProperty(
final String propertyName, final Token token, final String indent, final String bitSetName)
{
final StringBuilder sb = new StringBuilder();
final int offset = token.offset();
sb.append(String.format(
"\n" +
indent + " private final %s %s = new %s();\n",
bitSetName,
propertyName,
bitSetName
));
sb.append(String.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,
generateTypeFieldNotPresentCondition(token.version(), indent),
propertyName,
offset,
propertyName
));
return sb;
}
private CharSequence generateCompositeProperty(
final String propertyName, final Token token, final String indent, final String compositeName)
{
final int offset = token.offset();
final StringBuilder sb = new StringBuilder();
sb.append(String.format(
"\n" +
indent + " private final %s %s = new %s();\n",
compositeName,
propertyName,
compositeName
));
sb.append(String.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,
generateTypeFieldNotPresentCondition(token.version(), indent),
propertyName,
offset,
propertyName
));
return sb;
}
private String generateLiteral(final PrimitiveType type, final String value)
{
String literal = "";
final String castType = javaTypeName(type);
switch (type)
{
case CHAR:
case UINT8:
case INT8:
case INT16:
literal = "(" + castType + ")" + value;
break;
case UINT16:
case INT32:
literal = value;
break;
case UINT32:
literal = value + "L";
break;
case FLOAT:
if (value.endsWith("NaN"))
{
literal = "Float.NaN";
}
else
{
literal = value + "f";
}
break;
case INT64:
literal = value + "L";
break;
case UINT64:
literal = "0x" + Long.toHexString(Long.parseLong(value)) + "L";
break;
case DOUBLE:
if (value.endsWith("NaN"))
{
literal = "Double.NaN";
}
else
{
literal = value + "d";
}
}
return literal;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy