/** * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.web.bundle; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.util.HashMap; import java.util.Map; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import com.opengamma.OpenGammaRuntimeException; import com.opengamma.util.ArgumentChecker; /** * Parses a bundle XML file into a bundle manager. */ public class BundleParser { /** Logger. */ private static final Logger s_logger = LoggerFactory.getLogger(BundleParser.class); /** The bundle element tag name. */ private static final String BUNDLE_ELEMENT = "bundle"; /** The fragment element tag name. */ private static final String FRAGMENT_ELEMENT = "fragment"; /** The ID attribute name. */ private static final String ID_ATTR = "id"; /** * The URI provider for fragment references. */ private final UriProvider _fragmentUriProvider; /** * The base path. */ private final String _basePath; /** * The bundle manager to populate. */ private final BundleManager _bundleManager = new BundleManager(); /** * The cache of elements. */ private final Map<String, Element> _elementsByIdMap = new HashMap<String, Element>(); /** * Creates a parser * * @param fragmentUriProvider the URI provider for fragments, not null * @param basePath the base path, not null */ public BundleParser(UriProvider fragmentUriProvider, String basePath) { ArgumentChecker.notNull(fragmentUriProvider, "fragmentUriProvider"); ArgumentChecker.notNull(basePath, "basePath"); _fragmentUriProvider = fragmentUriProvider; _basePath = basePath.startsWith("/") ? basePath : "/" + basePath; } //------------------------------------------------------------------------- /** * Parses the XML file, returning the bundle manager. * * @param xmlStream the XML input stream, not null * @return the parsed bundle manager, not null */ public BundleManager parse(InputStream xmlStream) { ArgumentChecker.notNull(xmlStream, "xml inputstream"); DocumentBuilder builder = getDocumentBuilder(); if (builder != null) { try { Document document = builder.parse(xmlStream); processXMLDocument(document); } catch (SAXException ex) { throw new OpenGammaRuntimeException("unable to parse xml file", ex); } catch (IOException ex) { throw new OpenGammaRuntimeException("unable to read xml file", ex); } } return _bundleManager; } private void processXMLDocument(Document document) { buildAllElements(document); for (Element element : _elementsByIdMap.values()) { addToManager(element); } } private void addToManager(Element element) { String idAttr = element.getAttribute(ID_ATTR); Bundle bundle = new Bundle(idAttr); NodeList childNodes = element.getChildNodes(); for (int i = 0; i < childNodes.getLength(); i++) { Node node = childNodes.item(i); if (node.getNodeType() == Node.ELEMENT_NODE) { Element childElement = (Element) node; if (childElement.getNodeName().equals(BUNDLE_ELEMENT)) { processRefBundle(bundle, childElement); } if (childElement.getNodeName().equals(FRAGMENT_ELEMENT)) { processFragment(bundle, childElement); } } } _bundleManager.addBundle(bundle); } private void processFragment(Bundle bundle, Element element) { String fragment = element.getTextContent(); if (isValidFragment(fragment)) { bundle.addChildNode(createBundleFragment(fragment)); } } private boolean isValidFragment(String fragment) { if (StringUtils.isNotBlank(fragment)) { return true; } throw new OpenGammaRuntimeException("invalid fragment value while parsing bundle xml file"); } private BundleNode createBundleFragment(String fragment) { URI fragmentUri = getFragmentUriProvider().getUri(fragment); String fragmentPath = getBasePath() + fragment; return new Fragment(fragmentUri, fragmentPath); } private void processRefBundle(Bundle bundle, Element element) { String idRef = element.getAttribute("idref"); if (isValidIdRef(idRef)) { Bundle refBundle = _bundleManager.getBundle(idRef); if (refBundle == null) { Element refElement = _elementsByIdMap.get(idRef); // this can cause infinite loop if we have circular reference addToManager(refElement); refBundle = _bundleManager.getBundle(idRef); } bundle.addChildNode(refBundle); } } private boolean isValidIdRef(String idRef) { if (StringUtils.isNotBlank(idRef) && idRefExists(idRef)) { return true; } throw new OpenGammaRuntimeException(" invalid idref [" + idRef + "]"); } private boolean idRefExists(String idRef) { return _elementsByIdMap.get(idRef) != null; } private void buildAllElements(Document document) { Element rootElement = document.getDocumentElement(); if (isValidRootElement(rootElement)) { rootElement.normalize(); NodeList childNodes = rootElement.getChildNodes(); for (int i = 0; i < childNodes.getLength(); i++) { Node node = childNodes.item(i); if (node.getNodeType() == Node.ELEMENT_NODE) { Element element = (Element) node; if (isValidBundleElement(element)) { String idAttr = element.getAttribute(ID_ATTR); if (_elementsByIdMap.get(idAttr) == null) { _elementsByIdMap.put(idAttr, element); } else { throw new OpenGammaRuntimeException("parsing bundle XML : duplicate id attribute in " + node.getNodeName()); } } } } } } private boolean isValidRootElement(Element rootElement) { if (rootElement.getNodeName().equals("uiResourceConfig")) { return true; } throw new OpenGammaRuntimeException("parsing bundle XML : invalid root element " + rootElement.getNodeName()); } private boolean isValidBundleElement(Element element) { return isBundleElement(element) && hasChildren(element) && hasValidId(element); } private boolean hasValidId(Element element) { if (element.hasAttribute(ID_ATTR) && StringUtils.isNotBlank(element.getAttribute(ID_ATTR))) { return true; } throw new OpenGammaRuntimeException("parsing bundle XML : bundle element needs id attribute"); } private boolean hasChildren(Element element) { if (element.hasChildNodes()) { return true; } throw new OpenGammaRuntimeException("parsing bundle XML : missing children elements in bundle"); } private boolean isBundleElement(Element element) { if (element.getNodeName().equals(BUNDLE_ELEMENT)) { return true; } throw new OpenGammaRuntimeException("parsing bundle XML : element not a bundle"); } private DocumentBuilder getDocumentBuilder() { DocumentBuilder builder = null; DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); try { builder = builderFactory.newDocumentBuilder(); } catch (ParserConfigurationException e) { s_logger.warn("Unable to create a DOM parser", e); } return builder; } private UriProvider getFragmentUriProvider() { return _fragmentUriProvider; } private String getBasePath() { return _basePath; } }