package com.github.rmannibucau.cdi.configuration.xml; import com.github.rmannibucau.cdi.configuration.ConfigurationException; import com.github.rmannibucau.cdi.configuration.model.ConfigBean; import com.github.rmannibucau.cdi.configuration.xml.handlers.ArrayHandler; import com.github.rmannibucau.cdi.configuration.xml.handlers.ListHandler; import com.github.rmannibucau.cdi.configuration.xml.handlers.LookupHandler; import com.github.rmannibucau.cdi.configuration.xml.handlers.MapHandler; import com.github.rmannibucau.cdi.configuration.xml.handlers.NamespaceHandler; import com.github.rmannibucau.cdi.configuration.xml.handlers.PropertiesHandler; import com.github.rmannibucau.cdi.configuration.xml.handlers.PropertyHandler; import com.github.rmannibucau.cdi.configuration.xml.handlers.SetHandler; import com.github.rmannibucau.cdi.configuration.xml.handlers.WebServiceHandler; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.ServiceLoader; public final class ConfigParser extends DefaultHandler { private static final SAXParserFactory FACTORY = SAXParserFactory.newInstance(); private static final Map<String, NamespaceHandler> HANDLERS; static { FACTORY.setNamespaceAware(true); FACTORY.setValidating(false); // default handlers HANDLERS = new HashMap<String, NamespaceHandler>(5); // defaults for (final NamespaceHandler handler : new NamespaceHandler[] { new PropertyHandler(), new ListHandler(), new SetHandler(), new ArrayHandler(), new MapHandler(), new PropertiesHandler(), new LookupHandler(), new WebServiceHandler() }) { HANDLERS.put(handler.supportedUri(), handler); } // extensions for (final NamespaceHandler handler : ServiceLoader.load(NamespaceHandler.class)) { HANDLERS.put(handler.supportedUri(), handler); } } public static final String NAMESPACE_SCHEME = "cdi://"; private static interface Level { static final int ROOT = 0; static final int BEAN = 1; static final int ATTRIBUTE = 2; static final int REF = 3; } private Collection<ConfigBean> beans = new ArrayList<ConfigBean>(); private int level = 0; private ConfigBean bean = null; private StringBuilder text = null; private String ref = null; private String defaultQualifier = null; private String defaultScope = null; private ConfigParser() { // no-op } @Override public void startElement(final String uri, final String localName, final String qName, final Attributes attributes) throws SAXException { if (isDefaultNamespace(uri)) { if (level == Level.BEAN) { String scope = attributes.getValue("scope"); if (scope == null) { scope = defaultScope; } final String classname = attributes.getValue("class"); if (classname == null) { throw new ConfigurationException("class attribute can't be null"); } final boolean useConstructor = "true".equalsIgnoreCase(attributes.getValue("use-constructor")); String qualifier = attributes.getValue("qualifier"); if (qualifier == null) { qualifier = defaultQualifier; } bean = new ConfigBean(localName, classname, scope, qualifier, attributes.getValue("factory-class"), attributes.getValue("factory-method"), attributes.getValue("init-method"), attributes.getValue("destroy-method"), useConstructor); for (int i = 0; i < attributes.getLength(); i++) { final String attrUri = attributes.getURI(i); if (attrUri != null && !attrUri.isEmpty()) { final NamespaceHandler handler = findHandler(attrUri); if (handler != null) { handler.decorate(bean, attributes.getLocalName(i), attributes.getValue(i)); break; } } } } else if (level == Level.ATTRIBUTE) { text = new StringBuilder(); } else if (level == Level.ROOT) { defaultQualifier = attributes.getValue("default-qualifier"); defaultScope = attributes.getValue("default-scope"); if (defaultScope == null) { defaultScope = "dependent"; } } } else { final NamespaceHandler handler = findHandler(uri); if (handler == null) { throw new IllegalArgumentException("Can't find any handler for namespace " + uri); } if (level == Level.BEAN) { bean = handler.createBean(localName, attributes); if (bean != null) { beans.add(bean); } } else if (level > Level.BEAN) { handler.decorate(bean, localName, attributes); } } level++; } @Override public void characters(final char ch[], final int start, final int length) throws SAXException { if (text != null) { text.append(ch, start, length); } } @Override public void endElement(final String uri, final String localName, final String qName) throws SAXException { level--; if (isDefaultNamespace(uri)) { if (level == Level.BEAN) { beans.add(bean); bean = null; } else if (level == Level.ATTRIBUTE) { if (ref == null) { bean.getDirectAttributes().put(localName, text.toString()); } else { bean.getRefAttributes().put(localName, ref); } bean.getAttributeOrder().add(localName); ref = null; text = null; } else if (level == Level.REF) { ref = localName; } } } private boolean isDefaultNamespace(String uri) { return uri == null || uri.isEmpty(); } public static Collection<ConfigBean> parse(final InputStream is) throws ParserConfigurationException, SAXException, IOException { final ConfigParser handler = new ConfigParser(); final SAXParser parser = FACTORY.newSAXParser(); parser.parse(is, handler); return handler.beans; } private static NamespaceHandler findHandler(final String uri) { if (uri.startsWith(NAMESPACE_SCHEME)) { return HANDLERS.get(uri.substring(NAMESPACE_SCHEME.length())); } return HANDLERS.get(uri); } }