Java tutorial
/******************************************************************************* * Copyright (c) 2016 Prowide Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * Check the LGPL at <http://www.gnu.org/licenses/> for more details. *******************************************************************************/ package com.prowidesoftware.swift.io.parser; import java.io.ByteArrayInputStream; import java.util.logging.Level; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.Validate; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import com.prowidesoftware.swift.io.writer.FINWriterVisitor; import com.prowidesoftware.swift.model.SwiftBlock1; import com.prowidesoftware.swift.model.SwiftBlock2; import com.prowidesoftware.swift.model.SwiftBlock2Input; import com.prowidesoftware.swift.model.SwiftBlock2Output; import com.prowidesoftware.swift.model.SwiftBlock3; import com.prowidesoftware.swift.model.SwiftBlock4; import com.prowidesoftware.swift.model.SwiftBlock5; import com.prowidesoftware.swift.model.SwiftBlockUser; import com.prowidesoftware.swift.model.SwiftMessage; import com.prowidesoftware.swift.model.SwiftTagListBlock; import com.prowidesoftware.swift.model.Tag; import com.prowidesoftware.swift.model.UnparsedTextList; import com.prowidesoftware.swift.model.field.Field; /** * This is the main parser for WIFE's XML internal representation.<br> * The supported XML format is consider <i>internal</i> because it is an ad-hoc * defined XML structure for Swift messages, so it's not the SWIFT XML * Standard for FIN Messages.<br> * <br> * * This implementation should be used by calling some of the the conversion * services. * * @see com.prowidesoftware.swift.io.IConversionService * @since 5.0 * @author www.prowidesoftware.com */ public class XMLParser { private static final transient java.util.logging.Logger log = java.util.logging.Logger .getLogger(XMLParser.class.getName()); /** * Given a String containing a message in its WIFE internal XML * representation, returns a SwiftMessage object. * If there is any error during conversion this method returns <code>null</code> * @param xml the string containing the XML to parse * @return the XML parsed into a SwiftMessage object * * @see com.prowidesoftware.swift.io.IConversionService#getMessageFromXML(java.lang.String) */ public SwiftMessage parse(final String xml) { Validate.notNull(xml); try { final DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder(); final Document doc = db.parse(new ByteArrayInputStream(xml.getBytes())); return createMessage(doc); } catch (final Exception e) { log.log(Level.WARNING, "Error parsing XML", e); return null; } } /** * Helper method for XML representation parsing.<br> * * @param doc * Document object containing a message in XML format * @return SwiftMessage object populated with the given XML message data */ private SwiftMessage createMessage(final Document doc) { final NodeList messageNL = doc.getElementsByTagName("message"); if (messageNL.getLength() == 1) { final Node message = messageNL.item(0); final SwiftMessage m = new SwiftMessage(false); final NodeList blocksNL = message.getChildNodes(); if (log.isLoggable(Level.FINE)) { log.fine("blocks in message: " + blocksNL.getLength()); } for (int i = 0; i < blocksNL.getLength(); i++) { final Node blockNode = blocksNL.item(i); if (log.isLoggable(Level.FINE)) { log.fine("evaluating node " + blockNode.getNodeName()); } if (blockNode.getNodeType() == Node.ELEMENT_NODE) { final String blockName = blockNode.getNodeName(); if (blockName.equalsIgnoreCase("block1")) { m.setBlock1(getBlock1FromNode(blockNode)); } else if (blockName.equalsIgnoreCase("block2")) { m.setBlock2(getBlock2FromNode(blockNode)); } else if (blockName.equalsIgnoreCase("unparsedtexts")) { // unparsed texts at <message> level m.setUnparsedTexts(getUnparsedTextsFromNode(blockNode)); } else { // blocks 3, 4, 5 or user blocks m.addBlock(getTagListBlockFromNode(blockNode)); } } } // end block list iteration return m; } else { throw new IllegalArgumentException("<message> tag not found"); } } /** * Helper method for XML representation parsing.<br> * Given the <block1> node in the XML tree, returns the SwiftBlock1 object. * * @param blockNode Node object of the <block1> tag in the XML message * @return SwiftBlock1 object populated with the given portion of the XML message */ private SwiftBlock1 getBlock1FromNode(final Node blockNode) { final NodeList fields = blockNode.getChildNodes(); if (log.isLoggable(Level.FINE)) { log.fine(fields.getLength() + " children in <block1>"); } final SwiftBlock1 b1 = new SwiftBlock1(); for (int i = 0; i < fields.getLength(); i++) { final Node n = fields.item(i); if ("APPLICATIONID".equalsIgnoreCase(n.getNodeName())) { b1.setApplicationId(getText(n)); } else if ("SERVICEID".equalsIgnoreCase(n.getNodeName())) { b1.setServiceId(getText(n)); } else if ("LOGICALTERMINAL".equalsIgnoreCase(n.getNodeName())) { b1.setLogicalTerminal(getText(n)); } else if ("SESSIONNUMBER".equalsIgnoreCase(n.getNodeName())) { b1.setSessionNumber(getText(n)); } else if ("SEQUENCENUMBER".equalsIgnoreCase(n.getNodeName())) { b1.setSequenceNumber(getText(n)); } else if ("unparsedTexts".equalsIgnoreCase(n.getNodeName())) { b1.setUnparsedTexts(getUnparsedTextsFromNode(n)); } } return b1; } private String getText(final Node n) { String text = null; final Node c = n.getFirstChild(); if (c != null) { if (c.getNodeType() == Node.TEXT_NODE) { text = c.getNodeValue().trim(); } else { log.warning("Node is not TEXT_NODE: " + c); } } return text; } /** * Helper method for XML representation parsing.<br> * Given the <block2> node in the XML tree, returns the SwiftBlock1 object. * The method checks for the "type" attribute in the <block2> tag and * returns a SwiftBlock2Input or SwiftBlock2Output. * * @param blockNode Node object of the <block2> tag in the XML message * @return SwiftBlock2 object populated with the given portion of the XML message * @see #getBlock2InputFromNode(Node) * @see #getBlock2OutputFromNode(Node) */ private SwiftBlock2 getBlock2FromNode(final Node blockNode) { final String type = getNodeAttribute(blockNode, "type"); if (type == null) { log.severe("atrribute 'type' was expected but not found at <block2> xml tag"); return null; } else if (type.equals("input")) { return getBlock2InputFromNode(blockNode); } else if (type.equals("output")) { return getBlock2OutputFromNode(blockNode); } else { log.severe("expected 'input' or 'output' value for 'type' atribute at <block2> xml tag, and found: " + type); return null; } } /** * Helper method for XML representation parsing.<br> * Given the <block2 type="input"> node in the XML tree, returns the * SwiftBlock2Input object. * * @param blockNode Node object of the <block2> tag in the XML message * @return SwiftBlock2Input object populated with the given portion of the XML message */ private SwiftBlock2Input getBlock2InputFromNode(final Node blockNode) { final NodeList fields = blockNode.getChildNodes(); if (log.isLoggable(Level.FINE)) { log.fine(fields.getLength() + " childrens in <block2 type=\"input\">"); } final SwiftBlock2Input b2 = new SwiftBlock2Input(); for (int i = 0; i < fields.getLength(); i++) { final Node n = fields.item(i); if ("MESSAGETYPE".equalsIgnoreCase(n.getNodeName())) { b2.setMessageType(getText(n)); } else if ("RECEIVERADDRESS".equalsIgnoreCase(n.getNodeName())) { b2.setReceiverAddress(getText(n)); } else if ("MESSAGEPRIORITY".equalsIgnoreCase(n.getNodeName())) { b2.setMessagePriority(getText(n)); } else if ("DELIVERYMONITORING".equalsIgnoreCase(n.getNodeName())) { b2.setDeliveryMonitoring(getText(n)); } else if ("OBSOLESCENCEPERIOD".equalsIgnoreCase(n.getNodeName())) { b2.setObsolescencePeriod(getText(n)); } else if ("unparsedTexts".equalsIgnoreCase(n.getNodeName())) { b2.setUnparsedTexts(getUnparsedTextsFromNode(n)); } } return b2; } /** * Helper method for XML representation parsing.<br> * Given the <block2 type="output" node in the XML tree, returns the * SwiftBlock2Output object. * * @param blockNode Node object of the <block2> tag in the XML message * @return SwiftBlock2Output object populated with the given portion of the XML message */ private SwiftBlock2Output getBlock2OutputFromNode(final Node blockNode) { final NodeList fields = blockNode.getChildNodes(); if (log.isLoggable(Level.FINE)) { log.fine(fields.getLength() + " childrens in <block2 type=\"output\">"); } final SwiftBlock2Output b2 = new SwiftBlock2Output(); for (int i = 0; i < fields.getLength(); i++) { final Node n = fields.item(i); if ("MESSAGETYPE".equalsIgnoreCase(n.getNodeName())) { b2.setMessageType(getText(n)); } else if ("SENDERINPUTTIME".equalsIgnoreCase(n.getNodeName())) { b2.setSenderInputTime(getText(n)); } else if ("MIRDATE".equalsIgnoreCase(n.getNodeName())) { b2.setMIRDate(getText(n)); } else if ("MIRLOGICALTERMINAL".equalsIgnoreCase(n.getNodeName())) { b2.setMIRLogicalTerminal(getText(n)); } else if ("MIRSESSIONNUMBER".equalsIgnoreCase(n.getNodeName())) { b2.setMIRSessionNumber(getText(n)); } else if ("MIRSEQUENCENUMBER".equalsIgnoreCase(n.getNodeName())) { b2.setMIRSequenceNumber(getText(n)); } else if ("RECEIVEROUTPUTDATE".equalsIgnoreCase(n.getNodeName())) { b2.setReceiverOutputDate(getText(n)); } else if ("RECEIVEROUTPUTTIME".equalsIgnoreCase(n.getNodeName())) { b2.setReceiverOutputTime(getText(n)); } else if ("MESSAGEPRIORITY".equalsIgnoreCase(n.getNodeName())) { b2.setMessagePriority(getText(n)); } else if ("unparsedTexts".equalsIgnoreCase(n.getNodeName())) { b2.setUnparsedTexts(getUnparsedTextsFromNode(n)); } } return b2; } /** * Helper method for XML representation parsing.<br> * Given the <block3>, <block4>, <block5> or <block> (user block) node in * the XML tree, returns the corresponding SwiftTagListBlock object * populated with the given portion of the XML message. * * @param blockNode Node object of the <block3>, <block4>, <block5> or <block> tag in the XML message * @return SwiftTagListBlock object populated with the given portion of the XML message */ private SwiftTagListBlock getTagListBlockFromNode(final Node blockNode) { final String blockName = blockNode.getNodeName(); SwiftTagListBlock b = null; if (blockName.equalsIgnoreCase("block3")) { b = new SwiftBlock3(); } else if (blockName.equalsIgnoreCase("block4")) { b = new SwiftBlock4(); } else if (blockName.equalsIgnoreCase("block5")) { b = new SwiftBlock5(); } else if (blockName.equalsIgnoreCase("block")) { final String name = getNodeAttribute(blockNode, "name"); if (name != null) { b = new SwiftBlockUser(name); } else { b = new SwiftBlockUser(); } } else { return null; } final NodeList fields = blockNode.getChildNodes(); if (log.isLoggable(Level.FINE)) { log.fine(fields.getLength() + " children in tag list " + blockName); } for (int j = 0; j < fields.getLength(); j++) { final Node t = fields.item(j); if ("tag".equalsIgnoreCase(t.getNodeName())) { final Tag tag = getTag(t); b.append(tag); } else if ("field".equalsIgnoreCase(t.getNodeName())) { final Field field = getField(t); b.append(field); } else if ("unparsedtexts".equalsIgnoreCase(t.getNodeName())) { b.setUnparsedTexts(getUnparsedTextsFromNode(t)); } } return b; } /** * Helper method for XML representation parsing.<br> * Parses the given <tag> Node and returns a Tag object containing data from * the expected <name> and <value> tags. If name or value are not found as * children of the given node, the Tag object is returned with empty values. * * @param t the XML node to parse for name-value pair * @return a Tag object containing the name and value of the given XML node. */ private Tag getTag(final Node t) { final Tag tag = new Tag(); final NodeList children = t.getChildNodes(); for (int i = 0; i < children.getLength(); i++) { final Node n = children.item(i); /* * soportar ambas versiones de xml automagicamente */ if ("name".equalsIgnoreCase(n.getNodeName())) { tag.setName(getText(n)); } if ("value".equalsIgnoreCase(n.getNodeName())) { String text = getText(n); //normalize line feeds (DOM parser removes carriage return characters from original XML file) text = StringUtils.replace(text, "\n", FINWriterVisitor.SWIFT_EOL); tag.setValue(text); } else if ("unparsedtexts".equalsIgnoreCase(n.getNodeName())) { tag.setUnparsedTexts(getUnparsedTextsFromNode(n)); } } return tag; } /** * Helper method for XML representation parsing.<br> * Parses the given >field< Node and returns a Field object containing data from * the expected <name> and <component> inner elements. * If <name> element is not set it will return null. Otherwise it will return a Field * instance filled with content from <component> elements. * * @param t the XML node to parse for name-value pair * @return a Field object or null if "name" element is not present */ private Field getField(final Node t) { final NodeList children = t.getChildNodes(); String name = null; for (int i = 0; i < children.getLength(); i++) { final Node n = children.item(i); if ("name".equalsIgnoreCase(n.getNodeName())) { name = getText(n); break; } } if (name != null) { Field field = Field.getField(name, null); for (int i = 0; i < children.getLength(); i++) { final Node n = children.item(i); if ("component".equalsIgnoreCase(n.getNodeName())) { final String number = getNodeAttribute(n, "number"); if (StringUtils.isNumeric(number)) { String text = getText(n); //normalize line feeds (DOM parser removes carriage return characters from original XML file) text = StringUtils.replace(text, "\n", FINWriterVisitor.SWIFT_EOL); field.setComponent(Integer.valueOf(number), text); } } } return field; } return null; } /** * Helper method for XML representation parsing.<br> * Given the <unparsedtexts> node in the XML tree, returns an * UnparsedTextList object populated with the contents of the <text> child * tags of <unparsedtexts>. * * @param blockNode Node object of the <unparsedtexts> tag in the XML message * @return UnparsedTextList object populated with the given <text> tags content of the <unparsedtexts> */ private UnparsedTextList getUnparsedTextsFromNode(final Node blockNode) { final UnparsedTextList unparsedTexts = new UnparsedTextList(); final NodeList texts = blockNode.getChildNodes(); if (log.isLoggable(Level.FINE)) { log.fine(texts.getLength() + " children in <unparsedtexts>"); } for (int j = 0; j < texts.getLength(); j++) { final Node t = texts.item(j); if ("text".equalsIgnoreCase(t.getNodeName())) { unparsedTexts.addText(getText(t)); } } return unparsedTexts; } /** * Helper method for XML representation parsing.<br> * Gets the value of an expected attribute in a Node. * * @param n Node to analyze to find the attribute * @param attributeName the attribute name expected in the analyzed Node n * @return the value of the attribute expected, or null if the attribute was not found */ private String getNodeAttribute(final Node n, final String attributeName) { final Node attr = n.getAttributes().getNamedItem(attributeName); if ((attr == null) || !attr.getNodeName().equals(attributeName)) { return null; } else { return attr.getNodeValue(); } } }