diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/SbeTool.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/SbeTool.java
index 340de63f8f..6921c79db7 100644
--- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/SbeTool.java
+++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/SbeTool.java
@@ -60,6 +60,8 @@
*
sbe.keyword.append.token: Token to be appended to keywords.
* sbe.decode.unknown.enum.values: Support unknown decoded enum values. Defaults to false.
* sbe.xinclude.aware: Is XInclude supported for the schema. Defaults to false.
+ * sbe.type.package.override: Is a package attribute for types element supported (only for JAVA). Defaults to
+ * false.
*
*/
public class SbeTool
@@ -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 } 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.
*/
diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/TargetCodeGeneratorLoader.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/TargetCodeGeneratorLoader.java
index 07065da1ac..6f1e68f08c 100644
--- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/TargetCodeGeneratorLoader.java
+++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/TargetCodeGeneratorLoader.java
@@ -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;
@@ -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)),
+ new JavaOutputManager(outputDir, ir.applicableNamespace()));
}
},
diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java
index 21ca6f5a15..f8fa753d7f 100644
--- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java
+++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java
@@ -19,7 +19,7 @@
import org.agrona.MutableDirectBuffer;
import org.agrona.Strings;
import org.agrona.Verify;
-import org.agrona.generation.OutputManager;
+import org.agrona.generation.DynamicPackageOutputManager;
import org.agrona.sbe.*;
import uk.co.real_logic.sbe.PrimitiveType;
import uk.co.real_logic.sbe.generation.CodeGenerator;
@@ -30,7 +30,9 @@
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;
@@ -60,7 +62,7 @@ enum CodecType
private static final String INDENT = " ";
private final Ir ir;
- private final OutputManager outputManager;
+ private final DynamicPackageOutputManager outputManager;
private final String fqMutableBuffer;
private final String mutableBuffer;
private final String fqReadOnlyBuffer;
@@ -68,6 +70,32 @@ enum CodecType
private final boolean shouldGenerateGroupOrderAnnotation;
private final boolean shouldGenerateInterfaces;
private final boolean shouldDecodeUnknownEnumValues;
+ private final boolean shouldSupportTypePackages;
+ private final Set 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 DynamicPackageOutputManager outputManager)
+ {
+ this(ir, mutableBuffer, readOnlyBuffer, shouldGenerateGroupOrderAnnotation, shouldGenerateInterfaces,
+ shouldDecodeUnknownEnumValues, false, outputManager);
+ }
/**
* Create a new Java language {@link CodeGenerator}.
@@ -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(
@@ -87,12 +116,14 @@ public JavaGenerator(
final boolean shouldGenerateGroupOrderAnnotation,
final boolean shouldGenerateInterfaces,
final boolean shouldDecodeUnknownEnumValues,
- final OutputManager outputManager)
+ final boolean shouldSupportTypePackages,
+ final DynamicPackageOutputManager outputManager)
{
Verify.notNull(ir, "ir");
Verify.notNull(outputManager, "outputManager");
this.ir = ir;
+ this.shouldSupportTypePackages = shouldSupportTypePackages;
this.outputManager = outputManager;
this.mutableBuffer = validateBufferImplementation(mutableBuffer, MutableDirectBuffer.class);
@@ -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();
@@ -1188,6 +1238,7 @@ private void generateBitSet(final List tokens) throws IOException
final List choiceList = tokens.subList(1, tokens.size() - 1);
final String implementsString = implementsInterface(Flyweight.class.getSimpleName());
+ registerTypePackage(token, ir);
try (Writer out = outputManager.createOutput(decoderName))
{
final Encoding encoding = token.encoding();
@@ -1208,6 +1259,7 @@ private void generateBitSet(final List tokens) throws IOException
out.append("}\n");
}
+ registerTypePackage(token, ir);
try (Writer out = outputManager.createOutput(encoderName))
{
generateFixedFlyweightHeader(out, token, encoderName, implementsString, mutableBuffer, fqMutableBuffer);
@@ -1225,7 +1277,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));
}
@@ -1238,7 +1291,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));
}
@@ -1250,9 +1304,10 @@ private void generateEnum(final List 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));
final List valuesList = tokens.subList(1, tokens.size() - 1);
@@ -1272,6 +1327,7 @@ private void generateComposite(final List 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());
@@ -1320,6 +1376,7 @@ private void generateComposite(final List tokens) throws IOException
out.append("}\n");
}
+ registerTypePackage(token, ir);
try (Writer out = outputManager.createOutput(encoderName))
{
final String implementsString = implementsInterface(CompositeEncoderFlyweight.class.getSimpleName());
@@ -1572,13 +1629,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
{
@@ -1590,7 +1656,8 @@ private CharSequence generateMainHeader(
"package " + packageName + ";\n\n" +
(hasMutableBuffer ? "import " + fqMutableBuffer + ";\n" : "") +
(hasReadOnlyBuffer ? "import " + fqReadOnlyBuffer + ";\n" : "") +
- interfaceImportLine();
+ interfaceImportLine() +
+ packageImports.toString();
}
}
diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaOutputManager.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaOutputManager.java
new file mode 100644
index 0000000000..9dcd568c1f
--- /dev/null
+++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaOutputManager.java
@@ -0,0 +1,81 @@
+/*
+ * 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.DynamicPackageOutputManager;
+import org.agrona.generation.PackageOutputManager;
+
+/**
+ * Implementation of {@link DynamicPackageOutputManager} for Java.
+ */
+public class JavaOutputManager implements DynamicPackageOutputManager
+{
+ private final String baseDirName;
+ private final PackageOutputManager basePackageOutputManager;
+ private PackageOutputManager actingPackageOutputManager;
+ private final Object2ObjectHashMap outputManagerCache
+ = new Object2NullableObjectHashMap<>();
+
+ /**
+ * Constructor.
+ * @param baseDirName the target directory
+ * @param packageName the initial package name
+ */
+ public JavaOutputManager(final String baseDirName, final String packageName)
+ {
+ basePackageOutputManager = new PackageOutputManager(baseDirName, packageName);
+ actingPackageOutputManager = basePackageOutputManager;
+ this.baseDirName = baseDirName;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setPackageName(final String packageName)
+ {
+ actingPackageOutputManager = outputManagerCache.get(packageName);
+ if (actingPackageOutputManager == null)
+ {
+ actingPackageOutputManager = new PackageOutputManager(baseDirName, packageName);
+ outputManagerCache.put(packageName, actingPackageOutputManager);
+ }
+ }
+
+ private void resetPackage()
+ {
+ actingPackageOutputManager = basePackageOutputManager;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Writer createOutput(final String name) throws IOException
+ {
+ return new FilterWriter(actingPackageOutputManager.createOutput(name))
+ {
+ public void close() throws IOException
+ {
+ super.close();
+ resetPackage();
+ }
+ };
+ }
+}
diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/Token.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/Token.java
index d9a09966f2..3fdfb32674 100644
--- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/Token.java
+++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/Token.java
@@ -79,6 +79,7 @@ public class Token
private final String name;
private final String referencedName;
private final String description;
+ private final String packageName;
private final int id;
private final int version;
private final int deprecated;
@@ -94,6 +95,8 @@ public class Token
* @param name of the token in the message.
* @param referencedName of the type when created from a ref in a composite.
* @param description of what the token is for.
+ * @param packageName of the token in the message. Use null, except for BEGIN_MESSAGE tokens for types that
+ * require an explicit package.
* @param id as the identifier in the message declaration.
* @param version application within the template.
* @param deprecated as of this version.
@@ -107,6 +110,7 @@ public Token(
final String name,
final String referencedName,
final String description,
+ final String packageName,
final int id,
final int version,
final int deprecated,
@@ -123,6 +127,7 @@ public Token(
this.name = name;
this.referencedName = referencedName;
this.description = description;
+ this.packageName = packageName;
this.id = id;
this.version = version;
this.deprecated = deprecated;
@@ -152,6 +157,16 @@ public String name()
return name;
}
+ /**
+ * Return the packageName of the token
+ *
+ * @return packageName of the token or null, if it was not set explicitly.
+ */
+ public String packageName()
+ {
+ return packageName;
+ }
+
/**
* Get the name of the type when this is from a reference.
*
@@ -344,6 +359,7 @@ public String toString()
", name='" + name + '\'' +
", referencedName='" + referencedName + '\'' +
", description='" + description + '\'' +
+ ", packageName='" + packageName + '\'' +
", id=" + id +
", version=" + version +
", deprecated=" + deprecated +
@@ -361,6 +377,7 @@ public static class Builder
{
private Signal signal;
private String name;
+ private String packageName = null;
private String referencedName;
private String description;
private int id = INVALID_ID;
@@ -395,6 +412,19 @@ public Builder name(final String name)
return this;
}
+ /**
+ * Package name for the Token. Default is null. Use for BEGIN_MESSAGE tokens for types that require an explicit
+ * package.
+ *
+ * @param packageName for the Token.
+ * @return this for a fluent API.
+ */
+ public Builder packageName(final String packageName)
+ {
+ this.packageName = packageName;
+ return this;
+ }
+
/**
* Referenced type name for the Token.
*
@@ -515,6 +545,7 @@ public Token build()
name,
referencedName,
description,
+ packageName,
id,
version,
deprecated,
diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/xml/EncodedDataType.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/xml/EncodedDataType.java
index fc26d9cbdc..064b5358e6 100644
--- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/xml/EncodedDataType.java
+++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/xml/EncodedDataType.java
@@ -218,7 +218,32 @@ public EncodedDataType(
final int length,
final boolean varLen)
{
- super(name, presence, description, 0, 0, semanticType);
+ this(name, null, presence, description, semanticType, primitiveType, length, varLen);
+ }
+
+ /**
+ * Construct a new EncodedDataType with direct values.Does not handle constant values.
+ *
+ * @param name of the type
+ * @param packageName of the type
+ * @param presence of the type
+ * @param description of the type or null
+ * @param semanticType of the type or null
+ * @param primitiveType of the EncodedDataType
+ * @param length of the EncodedDataType
+ * @param varLen of the EncodedDataType
+ */
+ public EncodedDataType(
+ final String name,
+ final String packageName,
+ final Presence presence,
+ final String description,
+ final String semanticType,
+ final PrimitiveType primitiveType,
+ final int length,
+ final boolean varLen)
+ {
+ super(name, packageName, presence, description, 0, 0, semanticType);
this.primitiveType = primitiveType;
this.length = length;
diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/xml/IrGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/xml/IrGenerator.java
index 6ddf288605..6349ce760b 100644
--- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/xml/IrGenerator.java
+++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/xml/IrGenerator.java
@@ -213,6 +213,7 @@ private void add(final CompositeType type, final int currOffset, final Field fie
final Token.Builder builder = new Token.Builder()
.signal(Signal.BEGIN_COMPOSITE)
.name(type.name())
+ .packageName(type.packageName())
.referencedName(type.referencedName())
.offset(currOffset)
.size(type.encodedLength())
@@ -270,6 +271,7 @@ private void add(final EnumType type, final int offset, final Field field)
final Token.Builder builder = new Token.Builder()
.signal(Signal.BEGIN_ENUM)
.name(type.name())
+ .packageName(type.packageName())
.referencedName(type.referencedName())
.size(encodingType.size())
.offset(offset)
@@ -323,6 +325,7 @@ private void add(final SetType type, final int offset, final Field field)
final Token.Builder builder = new Token.Builder()
.signal(Signal.BEGIN_SET)
.name(type.name())
+ .packageName(type.packageName())
.referencedName(type.referencedName())
.size(encodingType.size())
.offset(offset)
@@ -372,6 +375,7 @@ private void add(final EncodedDataType type, final int offset, final int sinceVe
final Token.Builder tokenBuilder = new Token.Builder()
.signal(Signal.ENCODING)
.name(type.name())
+ .packageName(type.packageName())
.referencedName(type.referencedName())
.size(type.encodedLength())
.description(type.description())
@@ -424,6 +428,7 @@ private void add(final EncodedDataType type, final int offset, final Field field
final Token.Builder tokenBuilder = new Token.Builder()
.signal(Signal.ENCODING)
.name(type.name())
+ .packageName(type.packageName())
.referencedName(type.referencedName())
.size(type.encodedLength())
.description(type.description())
@@ -505,4 +510,4 @@ private Encoding.Presence mapPresence(final Presence presence)
return encodingPresence;
}
-}
\ No newline at end of file
+}
diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/xml/Type.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/xml/Type.java
index 8a1e491ff4..dba276f95f 100644
--- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/xml/Type.java
+++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/xml/Type.java
@@ -19,6 +19,7 @@
import static uk.co.real_logic.sbe.xml.XmlSchemaParser.getAttributeValue;
import static uk.co.real_logic.sbe.xml.XmlSchemaParser.getAttributeValueOrNull;
+import static uk.co.real_logic.sbe.xml.XmlSchemaParser.getTypesPackageAttribute;
/**
* An SBE type. One of encodedDataType, compositeType, enumType, or setType per the SBE spec.
@@ -26,6 +27,7 @@
public abstract class Type
{
private final String name;
+ private final String packageName;
private final Presence presence;
private final String description;
private final int deprecated;
@@ -54,7 +56,7 @@ public Type(final Node node, final String givenName, final String referencedName
}
this.referencedName = referencedName;
-
+ packageName = getTypesPackageAttribute(node);
presence = Presence.get(getAttributeValue(node, "presence", "required"));
description = getAttributeValueOrNull(node, "description");
sinceVersion = Integer.parseInt(getAttributeValue(node, "sinceVersion", "0"));
@@ -63,10 +65,32 @@ public Type(final Node node, final String givenName, final String referencedName
offsetAttribute = Integer.parseInt(getAttributeValue(node, "offset", "-1"));
}
+ /**
+ * Construct a new Type from direct values.
+ *
+ * @param name of the type
+ * @param presence of the type
+ * @param description of the type or null
+ * @param sinceVersion for the type
+ * @param deprecated version in which this was deprecated.
+ * @param semanticType of the type or null
+ */
+ public Type(
+ final String name,
+ final Presence presence,
+ final String description,
+ final int sinceVersion,
+ final int deprecated,
+ final String semanticType)
+ {
+ this(name, null, presence, description, sinceVersion, deprecated, semanticType);
+ }
+
/**
* Construct a new Type from direct values.
*
* @param name of the type
+ * @param packageName of the type
* @param presence of the type
* @param description of the type or null
* @param sinceVersion for the type
@@ -75,6 +99,7 @@ public Type(final Node node, final String givenName, final String referencedName
*/
public Type(
final String name,
+ final String packageName,
final Presence presence,
final String description,
final int sinceVersion,
@@ -82,6 +107,7 @@ public Type(
final String semanticType)
{
this.name = name;
+ this.packageName = packageName;
this.presence = presence;
this.description = description;
this.sinceVersion = sinceVersion;
@@ -206,4 +232,14 @@ public void offsetAttribute(final int offsetAttribute)
{
this.offsetAttribute = offsetAttribute;
}
+
+ /**
+ * Return the packageName attribute of the {@link Type} from the schema
+ *
+ * @return the packageName attribute value or null, if not explicitely defined by the schema
+ */
+ public String packageName()
+ {
+ return packageName;
+ }
}
diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/xml/XmlSchemaParser.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/xml/XmlSchemaParser.java
index fbc66f9888..455278d707 100644
--- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/xml/XmlSchemaParser.java
+++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/xml/XmlSchemaParser.java
@@ -326,6 +326,18 @@ public static String getAttributeValue(final Node elementNode, final String attr
return attrNode.getNodeValue();
}
+ /**
+ * To be used with child elements of {@code } elements. Returns the package attribute as
+ * defined on the parent {@code } element
+ *
+ * @param elementNode the node inside the types element
+ * @return the package name, or null if not defined
+ */
+ public static String getTypesPackageAttribute(final Node elementNode)
+ {
+ return getAttributeValue(elementNode.getParentNode(), "package", null);
+ }
+
/**
* Helper function that hides the null return from {@link org.w3c.dom.NamedNodeMap#getNamedItem(String)}.
*
diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/JavaGeneratorTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/JavaGeneratorTest.java
index 6ad49097bd..297fc02ec0 100644
--- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/JavaGeneratorTest.java
+++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/JavaGeneratorTest.java
@@ -481,6 +481,70 @@ void shouldMarkDeprecatedClasses() throws Exception
assertTrue(decoderClazz.isAnnotationPresent(Deprecated.class));
}
+ @Test
+ void shouldCreateTypesInDifferentPackages() throws Exception
+ {
+ final ParserOptions options = ParserOptions.builder().stopOnError(true).build();
+ final MessageSchema schema = parse(Tests.getLocalResource("explicit-package-test-schema.xml"), options);
+ final IrGenerator irg = new IrGenerator();
+ ir = irg.generate(schema);
+
+ outputManager.clear();
+ outputManager.setPackageName(ir.applicableNamespace());
+
+ final JavaGenerator generator = new JavaGenerator(ir, BUFFER_NAME, READ_ONLY_BUFFER_NAME, false, false, false,
+ true, outputManager);
+
+ generator.generate();
+ final String encoderFqcn = ir.applicableNamespace() + ".TestMessageEncoder";
+ final Class> encoderClazz = compile(encoderFqcn);
+ assertNotNull(encoderClazz);
+
+ final String decoderFqcn = ir.applicableNamespace() + ".TestMessageDecoder";
+ final Class> decoderClazz = compile(decoderFqcn);
+ assertNotNull(decoderClazz);
+
+ final Map sources = outputManager.getSources();
+ assertNotNull(sources.get("test.message.schema.common.CarEncoder"));
+ assertNotNull(sources.get("test.message.schema.common.CarDecoder"));
+ assertNotNull(sources.get("outside.schema.BooleanType"));
+ assertNotNull(sources.get("outside.schema.DaysEncoder"));
+ assertNotNull(sources.get("outside.schema.DaysDecoder"));
+ assertNotNull(sources.get(ir.applicableNamespace() + ".MessageHeaderEncoder"));
+ }
+
+ @Test
+ void shouldCreateTypesInSamePackageIfSupportDisabled() throws Exception
+ {
+ final ParserOptions options = ParserOptions.builder().stopOnError(true).build();
+ final MessageSchema schema = parse(Tests.getLocalResource("explicit-package-test-schema.xml"), options);
+ final IrGenerator irg = new IrGenerator();
+ ir = irg.generate(schema);
+
+ outputManager.clear();
+ outputManager.setPackageName(ir.applicableNamespace());
+
+ final JavaGenerator generator = new JavaGenerator(ir, BUFFER_NAME, READ_ONLY_BUFFER_NAME, false, false, false,
+ false, outputManager);
+
+ generator.generate();
+ final String encoderFqcn = ir.applicableNamespace() + ".TestMessageEncoder";
+ final Class> encoderClazz = compile(encoderFqcn);
+ assertNotNull(encoderClazz);
+
+ final String decoderFqcn = ir.applicableNamespace() + ".TestMessageDecoder";
+ final Class> decoderClazz = compile(decoderFqcn);
+ assertNotNull(decoderClazz);
+
+ final Map sources = outputManager.getSources();
+ assertNotNull(sources.get(ir.applicableNamespace() + ".CarEncoder"));
+ assertNotNull(sources.get(ir.applicableNamespace() + ".CarDecoder"));
+ assertNotNull(sources.get(ir.applicableNamespace() + ".BooleanType"));
+ assertNotNull(sources.get(ir.applicableNamespace() + ".DaysEncoder"));
+ assertNotNull(sources.get(ir.applicableNamespace() + ".DaysDecoder"));
+ assertNotNull(sources.get(ir.applicableNamespace() + ".MessageHeaderEncoder"));
+ }
+
private Class> getModelClass(final Object encoder) throws ClassNotFoundException
{
final String className = "Model";
diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/JavaOutputManagerTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/JavaOutputManagerTest.java
new file mode 100644
index 0000000000..3bbd7e4fb9
--- /dev/null
+++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/JavaOutputManagerTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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.File;
+import java.io.IOException;
+import java.io.Writer;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import org.junit.jupiter.api.Test;
+
+public class JavaOutputManagerTest
+{
+
+ private final String tempDirName = System.getProperty("java.io.tmpdir");
+
+ @Test
+ void shouldCreateFileWithinPackage() throws Exception
+ {
+ final String packageName = "uk.co.real_logic.test";
+ final String exampleClassName = "ExampleClassName";
+
+ final JavaOutputManager cut = new JavaOutputManager(tempDirName, packageName);
+ final Writer out = cut.createOutput(exampleClassName);
+ out.close();
+
+ final String typePackageName = "uk.co.real_logic.common";
+ final String typeClassName = "CompositeBigDecimal";
+ cut.setPackageName(typePackageName);
+ final Writer typeOut = cut.createOutput(typeClassName);
+ typeOut.close();
+
+ final String typePackageName2 = "uk.co.real_logic.common2";
+ final String typeClassName2 = "CompositeBigInteger";
+ cut.setPackageName(typePackageName2);
+ final Writer typeOut2 = cut.createOutput(typeClassName2);
+ typeOut2.close();
+
+ final String exampleClassName2 = "ExampleClassName2";
+
+ final Writer out2 = cut.createOutput(exampleClassName2);
+ out2.close();
+
+ assertFileExists(packageName, exampleClassName);
+ assertFileExists(packageName, exampleClassName2);
+ assertFileExists(typePackageName, typeClassName);
+ assertFileExists(typePackageName2, typeClassName2);
+ }
+
+ private void assertFileExists(final String packageName, final String exampleClassName) throws IOException
+ {
+ final String baseDirName = tempDirName.endsWith("" + File.separatorChar) ? tempDirName : tempDirName +
+ File.separatorChar;
+
+ final String fullyQualifiedFilename = baseDirName + packageName.replace('.', File.separatorChar) +
+ File.separatorChar + exampleClassName + ".java";
+
+ final Path path = FileSystems.getDefault().getPath(fullyQualifiedFilename);
+ final boolean exists = Files.exists(path);
+ Files.delete(path);
+
+ assertTrue(exists);
+ }
+
+}
diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/QualifiedYieldTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/QualifiedYieldTest.java
index 8f895f6a5d..701d50057a 100644
--- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/QualifiedYieldTest.java
+++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/QualifiedYieldTest.java
@@ -54,7 +54,7 @@ void shouldGenerateValidJava() throws Exception
final IrGenerator irg = new IrGenerator();
final Ir ir = irg.generate(schema);
final JavaGenerator generator = new JavaGenerator(
- ir, BUFFER_NAME, READ_ONLY_BUFFER_NAME, false, false, false, outputManager);
+ ir, BUFFER_NAME, READ_ONLY_BUFFER_NAME, false, false, false, false, outputManager);
outputManager.setPackageName(ir.applicableNamespace());
generator.generateMessageHeaderStub();
diff --git a/sbe-tool/src/test/resources/explicit-package-test-schema.xml b/sbe-tool/src/test/resources/explicit-package-test-schema.xml
new file mode 100644
index 0000000000..a0e02791b1
--- /dev/null
+++ b/sbe-tool/src/test/resources/explicit-package-test-schema.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 1
+
+
+ 0
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+
+
+
+
+
+
+
+
+
+