Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions sbe-tool/src/main/java/uk/co/real_logic/sbe/SbeTool.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@
* <li><b>sbe.keyword.append.token</b>: Token to be appended to keywords.</li>
* <li><b>sbe.decode.unknown.enum.values</b>: Support unknown decoded enum values. Defaults to false.</li>
* <li><b>sbe.xinclude.aware</b>: Is XInclude supported for the schema. Defaults to false.</li>
* <li><b>sbe.type.package.override</b>: Is a package attribute for types element supported (only for JAVA). Defaults to
* false.</li>
* </ul>
*/
public class SbeTool
Expand Down Expand Up @@ -109,6 +111,12 @@ public class SbeTool
*/
public static final String XINCLUDE_AWARE = "sbe.xinclude.aware";

/**
* Boolean system property to control the support of package names in {@code <types>} elements.
* Part of SBE v2-rc2. Defaults to false.
*/
public static final String TYPE_PACKAGE_OVERRIDE = "sbe.type.package.override";

/**
* Target language for generated code.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright 2013-2022 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;

import org.agrona.generation.OutputManager;

/**
* Extended version of the OutputManager allowing to set the java package. To be moved to agrona.
*/
public interface MultiPackageOutputManager extends OutputManager
{
/**
* Sets the current package name.
* @param packageName the packageName
*/
void setPackageName(String packageName);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/
package uk.co.real_logic.sbe.generation;

import org.agrona.generation.PackageOutputManager;
import uk.co.real_logic.sbe.generation.java.JavaOutputManager;
import uk.co.real_logic.sbe.generation.c.CGenerator;
import uk.co.real_logic.sbe.generation.c.COutputManager;
import uk.co.real_logic.sbe.generation.cpp.CppGenerator;
Expand Down Expand Up @@ -52,7 +52,8 @@ public CodeGenerator newInstance(final Ir ir, final String outputDir)
"true".equals(System.getProperty(JAVA_GROUP_ORDER_ANNOTATION)),
"true".equals(System.getProperty(JAVA_GENERATE_INTERFACES)),
"true".equals(System.getProperty(DECODE_UNKNOWN_ENUM_VALUES)),
new PackageOutputManager(outputDir, ir.applicableNamespace()));
"true".equals(System.getProperty(TYPE_PACKAGE_OVERRIDE)),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this default to true?

Copy link
Contributor Author

@ratcashdev ratcashdev Aug 10, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unless TYPE_PACKAGE_OVERRIDE is explicitly set to true, the generator will not respect package overrides, therefore behave like before this change - unless I am missing something here?

new JavaOutputManager(outputDir, ir.applicableNamespace()));
}
},

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import org.agrona.MutableDirectBuffer;
import org.agrona.Strings;
import org.agrona.Verify;
import org.agrona.generation.OutputManager;
import org.agrona.sbe.*;
import uk.co.real_logic.sbe.PrimitiveType;
import uk.co.real_logic.sbe.generation.CodeGenerator;
Expand All @@ -30,14 +29,17 @@
import java.io.Writer;
import java.util.ArrayList;
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.*;
import uk.co.real_logic.sbe.generation.MultiPackageOutputManager;

/**
* Generate codecs for the Java 8 programming language.
Expand All @@ -60,14 +62,40 @@ enum CodecType
private static final String INDENT = " ";

private final Ir ir;
private final OutputManager outputManager;
private final MultiPackageOutputManager 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 shouldSupportTypePackages;
private final Set<String> typePackages = 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 MultiPackageOutputManager outputManager)
{
this(ir, mutableBuffer, readOnlyBuffer, shouldGenerateGroupOrderAnnotation, shouldGenerateInterfaces,
shouldDecodeUnknownEnumValues, false, outputManager);
}

/**
* Create a new Java language {@link CodeGenerator}.
Expand All @@ -78,6 +106,7 @@ enum CodecType
* @param shouldGenerateGroupOrderAnnotation in the codecs.
* @param shouldGenerateInterfaces for common methods.
* @param shouldDecodeUnknownEnumValues generate support for unknown enum values when decoding.
* @param shouldSupportTypePackages generator support for types in their own package
* @param outputManager for generating the codecs to.
*/
public JavaGenerator(
Expand All @@ -87,12 +116,14 @@ public JavaGenerator(
final boolean shouldGenerateGroupOrderAnnotation,
final boolean shouldGenerateInterfaces,
final boolean shouldDecodeUnknownEnumValues,
final OutputManager outputManager)
final boolean shouldSupportTypePackages,
final MultiPackageOutputManager outputManager)
{
Verify.notNull(ir, "ir");
Verify.notNull(outputManager, "outputManager");

this.ir = ir;
this.shouldSupportTypePackages = shouldSupportTypePackages;
this.outputManager = outputManager;

this.mutableBuffer = validateBufferImplementation(mutableBuffer, MutableDirectBuffer.class);
Expand Down Expand Up @@ -144,11 +175,30 @@ public void generateTypeStubs() throws IOException
}
}

/**
* Register the the type's explicit package - if it's set and should be supported.
*
* @param token the 0-th token of the type
* @param ir the intermediate representation
* @return the overriden package name of the type if set and supported, or {@link Ir#applicableNamespace() }
*/
private String registerTypePackage(final Token token, final Ir ir)
{
if (shouldSupportTypePackages && token.packageName() != null)
{
typePackages.add(token.packageName());
outputManager.setPackageName(token.packageName());
return token.packageName();
}
return ir.applicableNamespace();
}

/**
* {@inheritDoc}
*/
public void generate() throws IOException
{
typePackages.clear();
generatePackageInfo();
generateTypeStubs();
generateMessageHeaderStub();
Expand Down Expand Up @@ -1188,6 +1238,7 @@ private void generateBitSet(final List<Token> tokens) throws IOException
final List<Token> messageBody = getMessageBody(tokens);
final String implementsString = implementsInterface(Flyweight.class.getSimpleName());

registerTypePackage(token, ir);
try (Writer out = outputManager.createOutput(decoderName))
{
generateFixedFlyweightHeader(out, token, decoderName, implementsString, readOnlyBuffer, fqReadOnlyBuffer);
Expand All @@ -1197,6 +1248,7 @@ private void generateBitSet(final List<Token> tokens) throws IOException
out.append("}\n");
}

registerTypePackage(token, ir);
try (Writer out = outputManager.createOutput(encoderName))
{
generateFixedFlyweightHeader(out, token, encoderName, implementsString, mutableBuffer, fqMutableBuffer);
Expand All @@ -1214,7 +1266,8 @@ private void generateFixedFlyweightHeader(
final String buffer,
final String fqBuffer) throws IOException
{
out.append(generateFileHeader(ir.applicableNamespace(), fqBuffer));
final String packageName = registerTypePackage(token, ir);
out.append(generateFileHeader(packageName, fqBuffer));
out.append(generateDeclaration(typeName, implementsString, token));
out.append(generateFixedFlyweightCode(typeName, token.encodedLength(), buffer));
}
Expand All @@ -1227,7 +1280,8 @@ private void generateCompositeFlyweightHeader(
final String fqBuffer,
final String implementsString) throws IOException
{
out.append(generateFileHeader(ir.applicableNamespace(), fqBuffer));
final String packageName = registerTypePackage(token, ir);
out.append(generateFileHeader(packageName, fqBuffer));
out.append(generateDeclaration(typeName, implementsString, token));
out.append(generateFixedFlyweightCode(typeName, token.encodedLength(), buffer));
}
Expand All @@ -1239,9 +1293,10 @@ private void generateEnum(final List<Token> tokens) throws IOException
final Encoding encoding = enumToken.encoding();
final String nullVal = encoding.applicableNullValue().toString();

final String packageName = registerTypePackage(enumToken, ir);
try (Writer out = outputManager.createOutput(enumName))
{
out.append(generateEnumFileHeader(ir.applicableNamespace()));
out.append(generateEnumFileHeader(packageName));
out.append(generateEnumDeclaration(enumName, enumToken));

out.append(generateEnumValues(getMessageBody(tokens), generateLiteral(encoding.primitiveType(), nullVal)));
Expand All @@ -1260,6 +1315,7 @@ private void generateComposite(final List<Token> tokens) throws IOException
final String decoderName = decoderName(compositeName);
final String encoderName = encoderName(compositeName);

registerTypePackage(token, ir);
try (Writer out = outputManager.createOutput(decoderName))
{
final String implementsString = implementsInterface(CompositeDecoderFlyweight.class.getSimpleName());
Expand Down Expand Up @@ -1308,6 +1364,7 @@ private void generateComposite(final List<Token> tokens) throws IOException
out.append("}\n");
}

registerTypePackage(token, ir);
try (Writer out = outputManager.createOutput(encoderName))
{
final String implementsString = implementsInterface(CompositeEncoderFlyweight.class.getSimpleName());
Expand Down Expand Up @@ -1560,13 +1617,22 @@ private CharSequence generateFileHeader(final String packageName, final String f
private CharSequence generateMainHeader(
final String packageName, final CodecType codecType, final boolean hasVarData)
{
final StringBuffer packageImports = new StringBuffer();
for (final String typePackage : typePackages)
{
packageImports.append("import ");
packageImports.append(typePackage);
packageImports.append(".*;\n");
}

if (fqMutableBuffer.equals(fqReadOnlyBuffer))
{
return
"/* Generated SBE (Simple Binary Encoding) message codec. */\n" +
"package " + packageName + ";\n\n" +
"import " + fqMutableBuffer + ";\n" +
interfaceImportLine();
interfaceImportLine() +
packageImports.toString();
}
else
{
Expand All @@ -1578,7 +1644,8 @@ private CharSequence generateMainHeader(
"package " + packageName + ";\n\n" +
(hasMutableBuffer ? "import " + fqMutableBuffer + ";\n" : "") +
(hasReadOnlyBuffer ? "import " + fqReadOnlyBuffer + ";\n" : "") +
interfaceImportLine();
interfaceImportLine() +
packageImports.toString();
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright 2013-2022 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 java.io.FilterWriter;
import java.io.IOException;
import java.io.Writer;
import org.agrona.collections.Object2NullableObjectHashMap;
import org.agrona.collections.Object2ObjectHashMap;
import org.agrona.generation.PackageOutputManager;
import uk.co.real_logic.sbe.generation.MultiPackageOutputManager;

/**
* Implementation of {@link MultiPackageOutputManager} for Java.
*/
public class JavaOutputManager implements MultiPackageOutputManager
{

private final String baseDirName;
private final PackageOutputManager global;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naming should be more explicit.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naming's difficult. But tried to do my best.

private PackageOutputManager acting;
private final Object2ObjectHashMap<String, PackageOutputManager> outputManagerCache
= new Object2NullableObjectHashMap<>();

/**
* Constructor.
* @param baseDirName the target directory
* @param packageName the initial package name
*/
public JavaOutputManager(final String baseDirName, final String packageName)
{
global = new PackageOutputManager(baseDirName, packageName);
acting = global;
this.baseDirName = baseDirName;
}

@Override
public void setPackageName(final String packageName)
{
acting = outputManagerCache.get(packageName);
if (acting == null)
{
acting = new PackageOutputManager(baseDirName, packageName);
outputManagerCache.put(packageName, acting);
}
}

private void resetPackage()
{
acting = global;
}

@Override
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@OverRide not used in code style.

public Writer createOutput(final String name) throws IOException
{
return new FilterWriter(acting.createOutput(name))
{
@Override
public void close() throws IOException
{
super.close();
resetPackage();
}
};
}
}
Loading