Skip to content

Commit c239921

Browse files
committed
[Java] Add support for transforming the schema to generate code for older versions.
1 parent 6275f89 commit c239921

11 files changed

+687
-6
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,5 @@ rust/Cargo.lock
113113
# Mac
114114
.DS_Store
115115
/sbe-tool/src/main/golang/uk_co_real_logic_sbe_ir_generated/
116+
117+
/generated/

sbe-tool/src/main/java/uk/co/real_logic/sbe/SbeTool.java

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,20 @@
1818
import org.agrona.DirectBuffer;
1919
import org.agrona.MutableDirectBuffer;
2020
import org.xml.sax.InputSource;
21-
import uk.co.real_logic.sbe.generation.*;
22-
import uk.co.real_logic.sbe.ir.*;
21+
import uk.co.real_logic.sbe.generation.CodeGenerator;
22+
import uk.co.real_logic.sbe.generation.TargetCodeGenerator;
23+
import uk.co.real_logic.sbe.generation.TargetCodeGeneratorLoader;
24+
import uk.co.real_logic.sbe.ir.Ir;
25+
import uk.co.real_logic.sbe.ir.IrDecoder;
26+
import uk.co.real_logic.sbe.ir.IrEncoder;
2327
import uk.co.real_logic.sbe.xml.*;
2428

25-
import java.io.*;
26-
import java.nio.file.*;
29+
import java.io.BufferedInputStream;
30+
import java.io.File;
31+
import java.io.InputStream;
32+
import java.nio.file.Files;
33+
import java.nio.file.Path;
34+
import java.nio.file.Paths;
2735

2836
/**
2937
* A tool for running the SBE parser, validator, and code generator.
@@ -176,6 +184,17 @@ public class SbeTool
176184
*/
177185
public static final String DECODE_UNKNOWN_ENUM_VALUES = "sbe.decode.unknown.enum.values";
178186

187+
/**
188+
* Configuration option used to manage sinceVersion based transformations. When set, parsed schemas will be
189+
* transformed to discard messages and types higher than the specified version. This can be useful when needing
190+
* to generate older versions of a schema to do version compatibility testing.
191+
* <p>
192+
* This field can contain a list of ordered pairs in the form:
193+
* <code>((&lt;schema id&gt; | '*') ':' &lt;schema id&gt;)(',' ((&lt;schema id&gt; | '*') ':' &lt;schema id&gt;))*</code>.
194+
* E.g. <code>123:5,*:6</code> which means transform schema with id = 123 to version 5, all others to version 6.
195+
*/
196+
public static final String SCHEMA_TRANSFORM_VERSION = "sbe.schema.transform.version";
197+
179198
/**
180199
* Main entry point for the SBE Tool.
181200
*
@@ -201,7 +220,10 @@ public static void main(final String[] args) throws Exception
201220
validateAgainstSchema(fileName, xsdFilename);
202221
}
203222

204-
ir = new IrGenerator().generate(parseSchema(fileName), System.getProperty(TARGET_NAMESPACE));
223+
final MessageSchema schema = parseSchema(fileName);
224+
final SchemaTransformer transformer = new SchemaTransformerFactory(
225+
System.getProperty(SCHEMA_TRANSFORM_VERSION));
226+
ir = new IrGenerator().generate(transformer.transform(schema), System.getProperty(TARGET_NAMESPACE));
205227
}
206228
else if (fileName.endsWith(".sbeir"))
207229
{
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2013-2022 Real Logic Limited.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package uk.co.real_logic.sbe.xml;
17+
18+
import java.util.Map;
19+
20+
class IdKeyedSchemaTransformer implements SchemaTransformer
21+
{
22+
private final Map<Integer, SchemaTransformer> transformerBySchemaId;
23+
private final SchemaTransformer defaultTransformer;
24+
25+
IdKeyedSchemaTransformer(
26+
final Map<Integer, SchemaTransformer> transformerBySchemaId,
27+
final SchemaTransformer defaultTransformer)
28+
{
29+
this.transformerBySchemaId = transformerBySchemaId;
30+
this.defaultTransformer = defaultTransformer;
31+
}
32+
33+
public MessageSchema transform(final MessageSchema originalSchema)
34+
{
35+
return lookupTransformer(originalSchema.id()).transform(originalSchema);
36+
}
37+
38+
SchemaTransformer lookupTransformer(final int schemaId)
39+
{
40+
return transformerBySchemaId.getOrDefault(schemaId, defaultTransformer);
41+
}
42+
}

sbe-tool/src/main/java/uk/co/real_logic/sbe/xml/MessageSchema.java

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
*/
1616
package uk.co.real_logic.sbe.xml;
1717

18-
import org.w3c.dom.Node;
1918
import org.agrona.Verify;
19+
import org.w3c.dom.Node;
2020

2121
import java.nio.ByteOrder;
2222
import java.util.*;
@@ -62,6 +62,28 @@ public class MessageSchema
6262
((CompositeType)typeByNameMap.get(this.headerType)).checkForWellFormedMessageHeader(schemaNode);
6363
}
6464

65+
MessageSchema(
66+
final String packageName,
67+
final String description,
68+
final int id,
69+
final int version,
70+
final String semanticVersion,
71+
final ByteOrder byteOrder,
72+
final String headerType,
73+
final Map<String, Type> typeByNameMap,
74+
final Map<Long, Message> messageByIdMap)
75+
{
76+
this.packageName = packageName;
77+
this.description = description;
78+
this.id = id;
79+
this.version = version;
80+
this.semanticVersion = semanticVersion;
81+
this.byteOrder = byteOrder;
82+
this.headerType = headerType;
83+
this.typeByNameMap = typeByNameMap;
84+
this.messageByIdMap = messageByIdMap;
85+
}
86+
6587
/**
6688
* The Schema headerType for message headers. This should be a {@link CompositeType}.
6789
*
@@ -154,6 +176,16 @@ public Collection<Message> messages()
154176
return messageByIdMap.values();
155177
}
156178

179+
/**
180+
* Get the {@link Collection} of {@link Type}s for this Schema.
181+
*
182+
* @return the {@link Collection} of {@link Type}s for this Schema.
183+
*/
184+
public Collection<Type> types()
185+
{
186+
return typeByNameMap.values();
187+
}
188+
157189
/**
158190
* Return the byte order specified by the messageSchema
159191
*
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright 2013-2022 Real Logic Limited.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package uk.co.real_logic.sbe.xml;
17+
18+
/**
19+
* An abstraction that allows for general transformations of the IR.
20+
*/
21+
public interface SchemaTransformer
22+
{
23+
/**
24+
* Does an identity transform of the incoming schema.
25+
*/
26+
SchemaTransformer IDENTITY_TRANSFORMER = originalSchema -> originalSchema;
27+
28+
/**
29+
* Return a new MessageSchema that has been transformed from the original.
30+
*
31+
* @param originalSchema to be transformed
32+
* @return resulting transformed schema.
33+
*/
34+
MessageSchema transform(final MessageSchema originalSchema);
35+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Copyright 2013-2022 Real Logic Limited.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package uk.co.real_logic.sbe.xml;
17+
18+
import java.util.HashMap;
19+
import java.util.Map;
20+
21+
/**
22+
* Entry point for schema transformations, will check all incoming configuration/properties and create the appropriate
23+
* transformers as required.
24+
*/
25+
public class SchemaTransformerFactory implements SchemaTransformer
26+
{
27+
private final SchemaTransformer transformer;
28+
29+
public SchemaTransformerFactory(final String schemaTransformConfig)
30+
{
31+
transformer = parse(schemaTransformConfig);
32+
}
33+
34+
/**
35+
* {@inheritDoc}
36+
*/
37+
public MessageSchema transform(final MessageSchema originalSchema)
38+
{
39+
return transformer.transform(originalSchema);
40+
}
41+
42+
static SchemaTransformer parse(final String configuration)
43+
{
44+
if (null == configuration || configuration.isEmpty())
45+
{
46+
return IDENTITY_TRANSFORMER;
47+
}
48+
49+
final String[] split = configuration.split(",");
50+
if (0 == split.length)
51+
{
52+
return IDENTITY_TRANSFORMER;
53+
}
54+
55+
final HashMap<Integer, SchemaTransformer> transformerBySchemaId = new HashMap<>();
56+
parseComponents(split, transformerBySchemaId);
57+
58+
SchemaTransformer defaultTransformer = transformerBySchemaId.remove(-1);
59+
defaultTransformer = null != defaultTransformer ? defaultTransformer : IDENTITY_TRANSFORMER;
60+
61+
return transformerBySchemaId.isEmpty() ?
62+
defaultTransformer : new IdKeyedSchemaTransformer(transformerBySchemaId, defaultTransformer);
63+
}
64+
65+
private static void parseComponents(
66+
final String[] configuration,
67+
final Map<Integer, SchemaTransformer> transformerBySchemaId)
68+
{
69+
for (String field : configuration)
70+
{
71+
final String[] fieldParts = field.split(":");
72+
73+
if (2 != fieldParts.length)
74+
{
75+
throw new IllegalArgumentException("version transformation property part '" + field + "' is invalid");
76+
}
77+
78+
final int schemaId = "*".equals(fieldParts[0]) ? -1 : Integer.parseInt(fieldParts[0].trim());
79+
final int sinceVersion = Integer.parseInt(fieldParts[1].trim());
80+
transformerBySchemaId.put(schemaId, new SinceVersionSchemaTransformer(sinceVersion));
81+
}
82+
}
83+
84+
SchemaTransformer delegate()
85+
{
86+
return transformer;
87+
}
88+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright 2013-2022 Real Logic Limited.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package uk.co.real_logic.sbe.xml;
17+
18+
import java.util.Collection;
19+
import java.util.HashMap;
20+
import java.util.Map;
21+
22+
class SinceVersionSchemaTransformer implements SchemaTransformer
23+
{
24+
private final int sinceVersion;
25+
26+
public SinceVersionSchemaTransformer(final int sinceVersion)
27+
{
28+
this.sinceVersion = sinceVersion;
29+
}
30+
31+
public MessageSchema transform(final MessageSchema originalSchema)
32+
{
33+
final Collection<Type> types = originalSchema.types();
34+
final Map<String, Type> newTypes = new HashMap<>();
35+
36+
for (Type type : types)
37+
{
38+
if (type.sinceVersion() <= this.sinceVersion)
39+
{
40+
newTypes.put(type.name(), type);
41+
}
42+
}
43+
44+
final Collection<Message> messages = originalSchema.messages();
45+
final Map<Long, Message> newMessages = new HashMap<>();
46+
for (Message message : messages)
47+
{
48+
if (message.sinceVersion() <= this.sinceVersion)
49+
{
50+
newMessages.put((long)message.id(), message);
51+
}
52+
}
53+
54+
return new MessageSchema(
55+
originalSchema.packageName(),
56+
originalSchema.description(),
57+
originalSchema.id(),
58+
sinceVersion,
59+
originalSchema.semanticVersion(),
60+
originalSchema.byteOrder(),
61+
originalSchema.messageHeader().name(),
62+
newTypes,
63+
newMessages);
64+
}
65+
66+
int sinceVersion()
67+
{
68+
return sinceVersion;
69+
}
70+
}

0 commit comments

Comments
 (0)