1
\$\begingroup\$

I have a class which makes calls to a static class. this class started off as a test utility class, primarily for performing operations on xml via XPath. Eventually some of the methods in the XmlUtil lass found use in the src code and the class was ported over. this resulted in the wrapper class TransformerUtil.

XmlUtil.java

public final class XmlUtil {

// Hidden constructor
private XmlUtil() {
}

/**
 * Get a DOM Document from XML String
 * 
 * @param xml
 * @return document
 * @throws ParserConfigurationException
 * @throws IOException
 * @throws SAXException
 */
public static Document getDocumentFromXml(final String xml)
        throws ParserConfigurationException, SAXException, IOException {
    final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    DocumentBuilder db;
    Document document = null;

    db = dbf.newDocumentBuilder();
    document = db.parse(new InputSource(new ByteArrayInputStream(xml.getBytes("UTF-8"))));

    return document;
}

/**
 * @param xpathString
 *            the xpath
 * @param xml
 *            the xml to search by xpath
 * @return result the string returned by xpath search
 * @throws XPathExpressionException
 * @throws IOException
 * @throws SAXException
 * @throws ParserConfigurationException
 */
public static String getStringByXPath(final String xpathString, final String xml)
        throws XPathExpressionException, ParserConfigurationException, SAXException, IOException {
    final Document document = getDocumentFromXml(xml);
    final XPath xpath = XPathFactory.newInstance().newXPath();
    final XPathExpression expr = xpath.compile(xpathString);
    return (String) expr.evaluate(document, XPathConstants.STRING);
}

/**
 * Retrieves element as Node object from xml document via XPath expression
 * 
 * @param xPathString XPath expression to locate desired element
 * @param document Document containing ACORD xml with desired element
 * @return Node returned by XPath search
 * @throws XPathExpressionException
 */
public static Node getNodeByXPath(final String xPathString, final Document document) throws XPathExpressionException {
    final XPath xpath = XPathFactory.newInstance().newXPath();
    final XPathExpression expr = xpath.compile(xPathString);
    return (Node)expr.evaluate(document, XPathConstants.NODE);
}

/**
 * Replaces value of node in xml document via XPath expression. Returns original xml if node
 * can't be found
 * 
 * @param xPathString XPath expression to locate element with value to be replaced
 * @param xml Transformed ACORD xml containing element with value to be replaced
 * @param value Replacement value for element
 * @return ACORD xml with selected element text replaced
 * @throws XPathExpressionException
 * @throws ParserConfigurationException
 * @throws SAXException
 * @throws IOException
 * @throws TransformerException
 */
public static String modifyNodeValueByXPath(final String xPathString, final String xml, final String value) 
        throws XPathExpressionException, ParserConfigurationException, SAXException, IOException, TransformerException {
    final Document document = getDocumentFromXml(xml);
    if(value != null){
        final Node resultNode = getNodeByXPath(xPathString, document);
        if(resultNode != null){
            resultNode.setTextContent(value);
        }
    }
    return convertDocumentToXmlString(document);

}

/**Transforms a Document object to a string
 * @param document Document object to be converted
 * @return String representation of Document objects contents
 * @throws TransformerFactoryConfigurationError
 * @throws TransformerConfigurationException
 * @throws TransformerException
 */
private static String convertDocumentToXmlString(final Document document) throws TransformerFactoryConfigurationError, TransformerConfigurationException, TransformerException {
    final DOMSource domSource = new DOMSource(document);
    final StringWriter writer = new StringWriter();
    final StreamResult result = new StreamResult(writer);
    final TransformerFactory tf = TransformerFactory.newInstance();
    final Transformer transformer = tf.newTransformer();
    transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
    transformer.transform(domSource, result);
    return writer.toString();
}

}

TransformerUtil.java

public class TransformerUtil{

private static final Integer FORM_CHARACTER_LENGTH = 7;
private static final String NOTIFICATION_REQUID_XPATH = "Root/VcReq/NotificationReq/ReqUID";

public TransformerUtil() {
}

public String getFormFromXml(final String xml) throws XPathExpressionException, ParserConfigurationException, SAXException, IOException {
    return XPathUtil.getStringByXPath(TransformerConstants.FORM_XPATH, xml);
}

public String getLOBFromXml(final String xml) throws XPathExpressionException, ParserConfigurationException, SAXException, IOException {
    return TransformerConstants.FORM_TO_LOB_MAPPING.get(StringUtils.left(getFormFromXml(xml), FORM_CHARACTER_LENGTH));
}

public String getMarketFromXml(final String xml) throws XPathExpressionException, ParserConfigurationException, SAXException, IOException {
    return XPathUtil.getStringByXPath(TransformerConstants.XPATH_MARKET, xml);
}

public String setUniqueIds(final String xml) throws XPathExpressionException, ParserConfigurationException, SAXException, IOException, TransformerException {
    String modifiedXml = xml;
    final String notificationReqUID = UUID.randomUUID().toString();
    modifiedXml = XPathUtil.modifyNodeValueByXPath(NOTIFICATION_REQUID_XPATH, modifiedXml, notificationReqUID);

    return modifiedXml;
}

public String translateXML(final String xml, final String xsl) throws TransformerException {

    Source xsltSource = new StreamSource(new StringReader(xsl.trim()));
    TransformerFactory transFactory = TransformerFactory.newInstance("net.sf.saxon.TransformerFactoryImpl", null);

    transFactory.setURIResolver(new URIResolver() {
        public Source resolve(String href, String base) throws TransformerException {
            InputStream is = TransformerUtilImpl.class.getResourceAsStream("/xsl/NI/" + href);
            return new StreamSource(is);
        }
    });

    Templates templates = transFactory.newTemplates(xsltSource);
    Transformer transformer = templates.newTransformer();

    StringWriter sw = new StringWriter();
    StreamSource streamSource = new StreamSource(new StringReader(xml.trim()));

    transformer.transform(streamSource, new StreamResult(sw));
    sw.flush();
    return sw.toString();
}

I'm unsure of how to proceed. this class is still useful in test code; the service these classes are used in involves xsl transforms and other xml manipulation, so the xmlUtil class makes things easier in tests based around xml manipulation. I'm also not certain that its functionaltiy should be moved entirely to TransformerUtil class either. It contaisn methods that are conceptually linked; Manipulating specifics of the xml. Adding in the low level functions that the existing methods make use of doesn't seem right. I've read in a few articles and books that static classes are a code smell, and should be removed, but I can't figure out the cleanest way to go. I'm also maintaining 2 versions of the code, src and test, but the class methods are used in both src and test.

\$\endgroup\$
  • \$\begingroup\$ Anyone? I'm leaning towards having the src version as its own object and used in transUtils, since its only used by the TransUtil class, and keeping the test version as a static test helper and let the two change as needed for each purpose. It means I'm technically implementing a double standard between src and test code(dealing with the code smell in src, having but I'm unsure of a better approach \$\endgroup\$ – jbailie1991 Mar 29 '17 at 10:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.