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 + + + + + + + + + +