
io.fixprotocol.orchestra.quickfix.DataDictionaryGenerator Maven / Gradle / Ivy
package io.fixprotocol.orchestra.quickfix;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import io.fixprotocol._2016.fixrepository.CodeSetType;
import io.fixprotocol._2016.fixrepository.CodeSets;
import io.fixprotocol._2016.fixrepository.CodeType;
import io.fixprotocol._2016.fixrepository.ComponentRefType;
import io.fixprotocol._2016.fixrepository.ComponentType;
import io.fixprotocol._2016.fixrepository.FieldRefType;
import io.fixprotocol._2016.fixrepository.FieldType;
import io.fixprotocol._2016.fixrepository.GroupRefType;
import io.fixprotocol._2016.fixrepository.GroupType;
import io.fixprotocol._2016.fixrepository.MessageType;
import io.fixprotocol._2016.fixrepository.PresenceT;
import io.fixprotocol._2016.fixrepository.Protocol;
import io.fixprotocol._2016.fixrepository.Repository;
/**
* Generates a QuickFIX data dictionary from a FIX Orchestra file
*
* This format is consumable by the C++, Java and .NET versions of QuickFIX.
* @author Don Mendelson
*
*/
public class DataDictionaryGenerator {
private class KeyValue {
final String key;
final T value;
public KeyValue(String key, T value) {
this.key = key;
this.value = value;
}
}
private static final int SPACES_PER_LEVEL = 2;
/**
* Runs a DataDictionaryGenerator with command line arguments
*
* The data dictionary format is consumable by QuickFIX, QuickFIX/J and QuickFIX/n.
*
* @param args command line arguments. The first argument is the name of a FIX Orchestra file. An
* optional second argument is the target directory for generated files. It defaults to
* directory "spec".
*/
public static void main(String[] args) {
DataDictionaryGenerator generator = new DataDictionaryGenerator();
if (args.length >= 1) {
File inputFile = new File(args[0]);
File outputDir;
if (args.length >= 2) {
outputDir = new File(args[1]);
} else {
outputDir = new File("spec");
}
generator.generate(inputFile, outputDir);
} else {
generator.usage();
}
}
private final Map codeSets = new HashMap<>();
private final Map components = new HashMap<>();
private final Map groups = new HashMap<>();
public void generate(File inputFile, File outputDir) {
try {
final Repository repository = unmarshal(inputFile);
final List codeSetsList = repository.getCodeSets();
for (CodeSets codeSetsCollection : codeSetsList) {
final List codeSetList = codeSetsCollection.getCodeSet();
for (CodeSetType codeSet : codeSetList) {
codeSets.put(codeSet.getName(), codeSet);
}
}
final List protocols = repository.getProtocol();
for (Protocol protocol : protocols) {
final List componentList = protocol.getComponents().getComponentOrGroup();
for (ComponentType component : componentList) {
if (component instanceof GroupType) {
groups.put(component.getId().intValue(), (GroupType) component);
} else {
components.put(component.getId().intValue(), component);
}
}
String version = protocol.getVersion();
// Split off EP portion of version in the form "FIX.5.0SP2_EP216"
String[] parts = version.split("_");
if (parts.length > 0) {
version = parts[0];
}
int major = 0;
int minor = 0;
String regex = "(FIX\\.)(?\\d+)(\\.)(?\\d+)(.*)";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(version);
if (matcher.find()) {
major = Integer.parseInt(matcher.group("major"));
minor = Integer.parseInt(matcher.group("minor"));
String versionPath = version.replaceAll("[\\.]", "");
File file = getSpecFilePath(outputDir, versionPath, ".xml");
outputDir.mkdirs();
try (FileWriter writer = new FileWriter(file)) {
writeElement(writer, "fix", 0, false, new KeyValue("major", major),
new KeyValue("minor", minor));
writeElement(writer, "header", 1, true);
writeElement(writer, "trailer", 1, true);
writeElement(writer, "messages", 1, false);
final List messageList = protocol.getMessages().getMessage();
for (MessageType messageType : messageList) {
writeMessage(writer, messageType);
}
writeElementEnd(writer, "messages", 1);
writeElement(writer, "components", 1, false);
for (ComponentType component : componentList) {
if (component instanceof GroupType) {
writeGroup(writer, (GroupType) component);
} else if (protocol.isHasComponents()) {
writeComponent(writer, component);
}
}
writeElementEnd(writer, "components", 1);
writeElement(writer, "fields", 1, false);
final List fieldList = repository.getFields().getField();
for (FieldType fieldType : fieldList) {
writeField(writer, fieldType);
}
writeElementEnd(writer, "fields", 1);
writeElementEnd(writer, "fix", 0);
}
} else {
System.err.format("Failed to parse FIX major and minor version in %s%n", version);
}
}
} catch (JAXBException | IOException e) {
e.printStackTrace();
}
}
private File getSpecFilePath(File outputDir, String versionPath, String extension) {
StringBuilder sb = new StringBuilder();
sb.append(versionPath);
sb.append(extension);
return new File(outputDir, sb.toString());
}
private String indent(int level) {
char[] chars = new char[level * SPACES_PER_LEVEL];
Arrays.fill(chars, ' ');
return new String(chars);
}
private boolean isAdmin(String category) {
return category != null && category.equals("Session");
}
private String toConstantName(String symbolicName) {
StringBuilder sb = new StringBuilder(symbolicName);
for (int i = symbolicName.length() - 1; i > 0; i--) {
if (Character.isUpperCase(sb.charAt(i)) && !Character.isUpperCase(sb.charAt(i - 1))) {
sb.insert(i, '_');
}
}
return sb.toString().toUpperCase();
}
private Repository unmarshal(File inputFile) throws JAXBException {
JAXBContext jaxbContext = JAXBContext.newInstance(Repository.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
return (Repository) jaxbUnmarshaller.unmarshal(inputFile);
}
private void usage() {
System.out.format("Usage: java %s ", this.getClass().getName());
}
private Writer writeCode(Writer writer, CodeType code) throws IOException {
writeElement(writer, "value", 3, true, new KeyValue("enum", code.getValue()),
new KeyValue("description", toConstantName(code.getName())));
return writer;
}
private Writer writeComponent(Writer writer, ComponentRefType componentRefType)
throws IOException {
writeElement(writer, "component", 3, true,
new KeyValue("name", componentRefType.getName()), new KeyValue("required",
componentRefType.getPresence().equals(PresenceT.REQUIRED) ? "Y" : "N"));
return writer;
}
private Writer writeComponent(Writer writer, ComponentType componentType) throws IOException {
writeElement(writer, "component", 2, false,
new KeyValue("name", componentType.getName()));
List