Java tutorial
/** * Copyright (c) 2011 Sebastian Tomac (tomac.org) * Licensed under LGPL licenses. * You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html **/ package org.tomac.tools.converter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import org.dom4j.Document; import org.dom4j.DocumentHelper; import org.dom4j.Element; import org.dom4j.io.OutputFormat; import org.dom4j.io.XMLWriter; import org.tomac.tools.converter.QuickFixField.QuickFixValue; import org.tomac.tools.converter.fixprotocol.FixProtocolUtils; import org.tomac.tools.converter.nordic.NordicUtils; public class XmlTranslator { private boolean isFixT = false; final ArrayList<QuickFixField> fixTFields = new ArrayList<QuickFixField>(); final ArrayList<QuickFixComponent> fixTComponents = new ArrayList<QuickFixComponent>(); private void addField(Element e, QuickFixField f) { e.addElement("field").addAttribute("name", f.name).addAttribute("required", f.reqd != null && f.reqd.equals("1") ? "Y" : "N"); } /* * <fix major="4" type="FIX" servicepack="0" minor="2"> <header> <field * name="BeginString" required="Y"/> <field name="BodyLength" required="Y"/> * .. </header> <messages> <message name="Heartbeat" msgcat="admin" * msgtype="0"> <field name="TestReqID" required="N"/> .. */ public Document createDocument(FixRepositoryDom fixDom) { final Document document = DocumentHelper.createDocument(); Element root; if (FixRepositoryToQuickFixXml.isStrictQuickFix) { if (fixDom.type.equals("FIXT")) isFixT = true; root = document.addElement("fix").addAttribute("major", fixDom.major) .addAttribute("minor", fixDom.minor).addAttribute("servicepack", fixDom.servicepack) .addAttribute("type", fixDom.type); } else { root = document.addElement("fix").addAttribute("major", fixDom.major) .addAttribute("minor", fixDom.minor).addAttribute("servicepack", fixDom.servicepack) .addAttribute("type", fixDom.type) .addAttribute("package", FixRepositoryToQuickFixXml.javaPackage) .addAttribute("flavour", FixRepositoryToQuickFixXml.fixFlavour); } final Element header = root.addElement("header"); if (!FixRepositoryToQuickFixXml.isStrictQuickFix || isFixT || Integer.valueOf(fixDom.major) < 5) { final ArrayList<QuickBase> qQ = new ArrayList<QuickBase>(); qQ.addAll(fixDom.quickFixHeader.fields); qQ.addAll(fixDom.quickFixHeader.components); Collections.sort(qQ); for (final QuickBase q : qQ) { if (q instanceof QuickFixField) { final QuickFixField f = (QuickFixField) q; fixTFields.add(f); addField(header, f); } if (q instanceof QuickFixComponent) { final QuickFixComponent cc = (QuickFixComponent) q; fixTComponents.add(cc); header.addElement("component").addAttribute("name", cc.name).addAttribute("required", cc.reqd.equals("0") ? "N" : "Y"); } } } final Element messages = root.addElement("messages"); for (final QuickFixMessage m : fixDom.quickFixMessages) { String name; // strict quickfix has separate xml for FIXT if (FixRepositoryToQuickFixXml.isStrictQuickFix && isFixT && !m.msgcat.equals("Session")) { continue; } else if (FixRepositoryToQuickFixXml.isStrictQuickFix && m.msgcat.equals("Session") && Integer.valueOf(fixDom.major) > 4) { continue; } if (m.name.equalsIgnoreCase("XML_non_FIX")) { // fixprotocol.org bug, empty message continue; } if (FixRepositoryToQuickFixXml.isNasdaqOMX && m.specialization != null) { final String n = m.specialization.replaceAll("-", "").replaceAll(" ", "").replaceAll("[(]fill[)]", "Fill"); if (n.length() > 0) { name = n; } else { name = m.name.replaceAll(" ", ""); } } else { name = m.name.replaceAll(" ", ""); } Element message; if (FixRepositoryToQuickFixXml.isNasdaqOMX && NordicUtils.isMessageWithSubMsgType(name)) { message = messages.addElement("message").addAttribute("name", name) .addAttribute("msgcat", getMsgCat(m.msgcat)).addAttribute("msgtype", m.msgtype) .addAttribute("msgsubtype", NordicUtils.getMessageSubMsgType(name)); } else { message = messages.addElement("message").addAttribute("name", name) .addAttribute("msgcat", getMsgCat(m.msgcat)).addAttribute("msgtype", m.msgtype); } final ArrayList<QuickBase> qQ = new ArrayList<QuickBase>(); qQ.addAll(m.fields); qQ.addAll(m.components); Collections.sort(qQ); for (final QuickBase q : qQ) { if (q instanceof QuickFixField) { final QuickFixField f = (QuickFixField) q; if (f.name.toLowerCase().contains("no longer used") || f.name.toLowerCase().contains("not defined")) { continue; } if (f.type.equalsIgnoreCase("NUMINGROUP")) { continue; } addField(message, f); fixTFields.add(f); } // <component name="Parties" required="N"/> if (q instanceof QuickFixComponent) { final QuickFixComponent c = (QuickFixComponent) q; if (c.name.equals("StandardHeader") || c.name.equals("StandardTrailer")) { continue; } if (FixRepositoryToQuickFixXml.isNasdaqOMX && (c.name.equalsIgnoreCase("LegPreAllocGrp") || c.name.equalsIgnoreCase("PreAllocGrp") || c.name.equalsIgnoreCase("TrdAllocGrp"))) { continue; // jet another Nasdaq OMX bug } fixTComponents.add(c); message.addElement("component").addAttribute("name", c.name).addAttribute("required", c.reqd.equals("0") ? "N" : "Y"); } } } final Element trailer = root.addElement("trailer"); if (!FixRepositoryToQuickFixXml.isStrictQuickFix || isFixT || Integer.valueOf(fixDom.major) < 5) { for (final QuickFixField f : fixDom.quickFixTrailer.fields) { fixTFields.add(f); addField(trailer, f); } } final Element components = root.addElement("components"); for (final QuickFixComponent c : fixDom.quickFixComponents) { if (c.name.equals("StandardHeader")) { continue; } if (c.name.equals("StandardTrailer")) { continue; } // ugh hardcoded components for FIXT // strict quickfix has separate xml for FIXT if (FixRepositoryToQuickFixXml.isStrictQuickFix && isFixT && !c.name.equals("HopGrp") && !c.name.equals("MsgTypeGrp")) { continue; } else if (FixRepositoryToQuickFixXml.isStrictQuickFix && (c.name.equals("HopGrp") || c.name.equals("MsgTypeGrp")) && Integer.valueOf(fixDom.major) > 4) { continue; } if (FixRepositoryToQuickFixXml.isNasdaqOMX && (c.name.equalsIgnoreCase("LegPreAllocGrp") || c.name.equalsIgnoreCase("PreAllocGrp") || c.name.equalsIgnoreCase("TrdAllocGrp"))) { continue; // jet another Nasdaq OMX bug } for (QuickFixField f : c.fields) fixTFields.add(f); final Element component = components.addElement("component").addAttribute("name", c.name); final ArrayList<QuickBase> qq = new ArrayList<QuickBase>(); qq.addAll(c.fields); Collections.sort(qq); QuickFixField qf = null; for (final QuickBase q : qq) { if (q instanceof QuickFixField) { qf = (QuickFixField) q; break; } } final Element group = c.isRepeating ? component.addElement("group").addAttribute("name", qf.name) .addAttribute("required", qf.reqd.equals("0") ? "N" : "Y") : component; final ArrayList<QuickBase> qQ = new ArrayList<QuickBase>(); qQ.addAll(c.fields); qQ.addAll(c.components); // nordic bug NestedParties, NestedParties2.. us Parties fields if (FixRepositoryToQuickFixXml.isNasdaqOMX && c.name.contains("NestedParties")) { QuickFixComponent cParties = fixDom.quickFixNamedComponents.get("Parties"); qQ.addAll(cParties.fields); qQ.addAll(cParties.components); } Collections.sort(qQ); for (final QuickBase q : qQ) { if (q instanceof QuickFixField) { final QuickFixField f = (QuickFixField) q; if (f.name.toLowerCase().contains(" ")) { continue; } if (f.type.equalsIgnoreCase("NUMINGROUP")) { continue; } group.addElement("field").addAttribute("name", f.name).addAttribute("number", f.number) .addAttribute("type", getType(f.type)) .addAttribute("required", f.reqd != null && f.reqd.equals("1") ? "Y" : "N"); } if (q instanceof QuickFixComponent) { final QuickFixComponent cc = (QuickFixComponent) q; if (FixRepositoryToQuickFixXml.isNasdaqOMX && (cc.name.equalsIgnoreCase("LegPreAllocGrp") || cc.name.equalsIgnoreCase("PreAllocGrp") || cc.name.equalsIgnoreCase("TrdAllocGrp"))) { continue; // jet another Nasdaq OMX bug } group.addElement("component").addAttribute("name", cc.name).addAttribute("required", cc.reqd.equals("0") ? "N" : "Y"); } } } final Element fields = root.addElement("fields"); if (FixRepositoryToQuickFixXml.isStrictQuickFix) { for (final QuickFixField f : fixTFields) { addField(f, fields); } } else { for (final QuickFixField f : fixDom.quickFixFields) { addField(f, fields); } } return document; } HashMap<String, QuickFixField> fieldsUnique = new HashMap<String, QuickFixField>(); private void addField(QuickFixField f, Element fields) { if (f.name.toLowerCase().contains(" ")) { return; } if (null != fieldsUnique.put(f.name, f)) { return; // field already added } Element field; if (FixRepositoryToQuickFixXml.isNasdaqOMX && NordicUtils.isTagWithLength(f.number)) { field = fields.addElement("field").addAttribute("name", f.name).addAttribute("number", f.number) .addAttribute("type", getType(f.type)) .addAttribute("length", NordicUtils.getTagLength(f.number)); } else if (FixProtocolUtils.isTagWithLength(f.number)) { field = fields.addElement("field").addAttribute("name", f.name).addAttribute("number", f.number) .addAttribute("type", getType(f.type)) .addAttribute("length", FixProtocolUtils.getTagLength(f.number)); } else { field = fields.addElement("field").addAttribute("name", f.name).addAttribute("number", f.number) .addAttribute("type", getType(f.type)); } HashMap<String, QuickFixValue> quickFixNamedValue = new HashMap<String, QuickFixValue>(); for (final QuickFixValue v : f.quickFixValues) { quickFixNamedValue.put(v.fixEnum, v); } // NasdaqOMX bug if (FixRepositoryToQuickFixXml.isNasdaqOMX && f.name.equalsIgnoreCase("ExecTransType")) { for (final QuickFixValue v : NordicUtils.getExecTransAdditionalTypes(f)) { quickFixNamedValue.put(v.fixEnum, v); } } for (final QuickFixValue v : quickFixNamedValue.values()) { if (v.fixEnum.length() == 0) continue; // NasdaqOMX bug field.addElement("value").addAttribute("enum", v.fixEnum.replaceAll("[\\s\\xA0]", "")) .addAttribute("description", getDescription(v.description, v.fixEnum)); } } private String getDescription(String description, String fixEnum) { if (FixRepositoryToQuickFixXml.isNasdaqOMX) { // NasdaqOMX bug messed up comments RequiredTagMissing -> "Required tag missing" if (description.equalsIgnoreCase("RequiredTagMissing")) description = "Required Tag Missing"; if (description.equalsIgnoreCase("InvalidTagNumber")) description = "Invalid Tag Number"; if (description.equalsIgnoreCase("IncorrectNumInGroupCountForRepeatingGroup")) description = "Incorrect NumInGroup Count For Repeating Group"; if (description.equalsIgnoreCase("ValueIsIncorrectOutOfRangeForThisTag")) description = "Value Is Incorrect Out Of Range For This Tag"; if (description.equalsIgnoreCase("TagSpecifiedWithoutAValue")) description = "Tag Specified Without A Value"; if (description.equalsIgnoreCase("UndefinedTag")) description = "Undefined Tag"; } String tmp = description.toUpperCase().replaceAll(" ", "_").replaceAll("__", "_").replaceAll("[^0-9A-Z_]", ""); if (tmp.substring(0, 1).matches("[0-9]")) tmp = "I" + tmp; // to long for name differentiation if (tmp.contains("EXACT_MATCH_ON_TRADE_DATE_STOCK_SYMBOL_QUANTITY")) { tmp = tmp.replace("EXACT_MATCH_ON_TRADE_DATE_STOCK_SYMBOL_QUANTITY", ""); } if (FixRepositoryToQuickFixXml.isNasdaqOMX && tmp.contains("EXACTMATCHONTRADEDATESTOCKSYMBOLQUANTITYPRICETRADETYPEANDSPECIAL")) { tmp = tmp.replace("EXACTMATCHONTRADEDATESTOCKSYMBOLQUANTITY", ""); } if (FixRepositoryToQuickFixXml.isNasdaqOMX && tmp.contains("REGISTEREDADDRESS")) { tmp = tmp + fixEnum; } return tmp.substring(0, tmp.length() > 64 ? 64 : tmp.length()); } private String getMsgCat(String msgcat) { if (msgcat.equals("Session")) { return "admin"; } return "app"; } private String getType(String type) { if (type.toUpperCase().equals("MULTIPLEVALUESTRING")) return "MULTIPLESTRINGVALUE"; // Nasdaq OMX bug return type.toUpperCase(); } public void translate(FixRepositoryDom fixDom, File outputDir) throws IOException { final Document doc = createDocument(fixDom); write(doc, outputDir); } public void write(Document document, File outputDir) throws IOException { // lets write to a file final OutputFormat format = OutputFormat.createPrettyPrint(); XMLWriter writer = new XMLWriter(new FileWriter(outputDir), format); writer.write(document); writer.close(); // Pretty print the document to System.out writer = new XMLWriter(System.out, format); writer.write(document); } }