package org.nutz.ioc.loader.xml; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; import javax.xml.parsers.DocumentBuilder; import org.nutz.ioc.IocLoader; import org.nutz.ioc.IocLoading; import org.nutz.ioc.Iocs; import org.nutz.ioc.ObjectLoadException; import org.nutz.ioc.meta.IocEventSet; import org.nutz.ioc.meta.IocField; import org.nutz.ioc.meta.IocObject; import org.nutz.ioc.meta.IocValue; import org.nutz.json.Json; import org.nutz.lang.Lang; import org.nutz.lang.Streams; import org.nutz.lang.Strings; import org.nutz.log.Log; import org.nutz.log.Logs; import org.nutz.resource.NutResource; import org.nutz.resource.Scans; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * 使用XML做为Ioc配置文件 <br> * 限制: <<br> * <li>必须是良构的XML文件 <li> <li>obj必须定义type,当前实现中IocObject是共享的 <li> * * @author wendal(wendal1985@gmail.com) * @version 2.0 */ public class XmlIocLoader implements IocLoader { private static final Log LOG = Logs.get(); protected Map<String, IocObject> iocMap = new LinkedHashMap<String, IocObject>(); protected Map<String, String> parentMap = new TreeMap<String, String>(); protected static final String TAG_OBJ = "obj"; protected static final String TAG_ARGS = "args"; protected static final String TAG_FIELD = "field"; protected static final String TAG_FACTORY = "factory"; /** * 仅用于标示内部obj的id,内部obj声明的id将被忽略 <b<br> * 该设计基于内部obj也可以使用继承顶层的obj */ private static int innerId; public XmlIocLoader(String... fileNames) { try { DocumentBuilder builder = Lang.xmls(); Document document; List<NutResource> list = Scans.me().loadResource(getScanPatten(), fileNames); for (NutResource nr : list) { InputStream ins = nr.getInputStream(); document = builder.parse(ins); document.normalizeDocument(); NodeList nodeListZ = ((Element) document.getDocumentElement()).getChildNodes(); for (int i = 0; i < nodeListZ.getLength(); i++) { if (nodeListZ.item(i) instanceof Element) paserBean((Element) nodeListZ.item(i), false); } Streams.safeClose(ins); } handleParent(); if (LOG.isDebugEnabled()) LOG.debugf("Load complete :\n%s", Json.toJson(iocMap)); } catch (Throwable e) { throw Lang.wrapThrow(e); } } public String[] getName() { return iocMap.keySet().toArray(new String[iocMap.keySet().size()]); } public boolean has(String name) { return iocMap.containsKey(name); } public IocObject load(IocLoading loading, String name) throws ObjectLoadException { if (has(name)) return iocMap.get(name); throw new ObjectLoadException("Object '" + name + "' without define!"); } protected String paserBean(Element beanElement, boolean innerBean) throws Throwable { String beanId; if (innerBean) { beanId = "inner$" + innerId; innerId++; } else beanId = beanElement.getAttribute("name"); if (beanId == null) throw Lang.makeThrow("No name for one bean!"); if (iocMap.containsKey(beanId)) throw Lang.makeThrow("Name of bean is not unique! name=" + beanId); if (LOG.isDebugEnabled()) LOG.debugf("Resolving bean define, name = %s", beanId); IocObject iocObject = new IocObject(); String beanType = beanElement.getAttribute("type"); if (!Strings.isBlank(beanType)) iocObject.setType(Lang.loadClass(beanType)); String beanScope = beanElement.getAttribute("scope"); if (!Strings.isBlank(beanScope)) iocObject.setScope(beanScope); String beanParent = beanElement.getAttribute("parent"); if (!Strings.isBlank(beanParent)) parentMap.put(beanId, beanParent); String factory = beanElement.getAttribute("factory"); if (!Strings.isBlank(factory)) iocObject.setFactory(factory); parseArgs(beanElement, iocObject); parseFields(beanElement, iocObject); parseEvents(beanElement, iocObject); iocMap.put(beanId, iocObject); if (LOG.isDebugEnabled()) LOG.debugf("Resolved bean define, name = %s", beanId); return beanId; } protected void parseArgs(Element beanElement, IocObject iocObject) throws Throwable { List<Element> list = getChildNodesByTagName(beanElement, TAG_ARGS); if (list.size() > 0) { Element argsElement = list.get(0); NodeList argNodeList = argsElement.getChildNodes(); for (int i = 0; i < argNodeList.getLength(); i++) { if (argNodeList.item(i) instanceof Element) iocObject.addArg(parseX((Element) argNodeList.item(i))); } } } protected void parseFields(Element beanElement, IocObject iocObject) throws Throwable { List<Element> list = getChildNodesByTagName(beanElement, TAG_FIELD); for (Element fieldElement : list) { IocField iocField = new IocField(); iocField.setName(fieldElement.getAttribute("name")); if (fieldElement.hasChildNodes()) { NodeList nodeList = fieldElement.getChildNodes(); for (int j = 0; j < nodeList.getLength(); j++) { if (nodeList.item(j) instanceof Element) { iocField.setValue(parseX((Element) nodeList.item(j))); break; } } } iocObject.addField(iocField); } } protected static final String STR_TAG = "str"; protected static final String ARRAY_TAG = "array"; protected static final String MAP_TAG = "map"; protected static final String ITEM_TAG = "item"; protected static final String LIST_TAG = "list"; protected static final String SET_TAG = "set"; protected static final String OBJ_TAG = "obj"; protected static final String INT_TAG = "int"; protected static final String SHORT_TAG = "short"; protected static final String LONG_TAG = "long"; protected static final String FLOAT_TAG = "float"; protected static final String DOUBLE_TAG = "double"; protected static final String BOOLEAN_TAG = "bool"; protected static final String REFER_TAG = IocValue.TYPE_REFER; protected static final String JAVA_TAG = IocValue.TYPE_JAVA; protected static final String FILE_TAG = IocValue.TYPE_FILE; protected static final String EVN_TAG = IocValue.TYPE_ENV; protected static final String JNDI_TAG = IocValue.TYPE_JNDI; protected static final String SYS_TAG = IocValue.TYPE_SYS; protected static final String APP_TAG = IocValue.TYPE_APP; protected IocValue parseX(Element element) throws Throwable { IocValue iocValue = new IocValue(); String type = element.getNodeName(); if (EVN_TAG.equalsIgnoreCase(type)) { iocValue.setType(EVN_TAG); iocValue.setValue(element.getTextContent()); } else if (SYS_TAG.equalsIgnoreCase(type)) { iocValue.setType(SYS_TAG); iocValue.setValue(element.getTextContent()); } else if (JNDI_TAG.equalsIgnoreCase(type)) { iocValue.setType(JNDI_TAG); iocValue.setValue(element.getTextContent()); } else if (JAVA_TAG.equalsIgnoreCase(type)) { iocValue.setType(JAVA_TAG); iocValue.setValue(element.getTextContent()); } else if (REFER_TAG.equalsIgnoreCase(type)) { iocValue.setType(REFER_TAG); iocValue.setValue(element.getTextContent()); } else if (FILE_TAG.equalsIgnoreCase(type)) { iocValue.setType(FILE_TAG); iocValue.setValue(element.getTextContent()); } else if (APP_TAG.equalsIgnoreCase(type)) { iocValue.setType(APP_TAG); iocValue.setValue(element.getTextContent()); } else if (OBJ_TAG.equalsIgnoreCase(type)) { iocValue.setType(REFER_TAG); iocValue.setValue(paserBean(element, true)); } else if (MAP_TAG.equalsIgnoreCase(type)) { iocValue.setType(null); iocValue.setValue(paserMap(element)); } else if (LIST_TAG.equalsIgnoreCase(type)) { iocValue.setType(null); iocValue.setValue(paserCollection(element)); } else if (ARRAY_TAG.equalsIgnoreCase(type)) { iocValue.setType(null); iocValue.setValue(paserCollection(element).toArray()); } else if (SET_TAG.equalsIgnoreCase(type)) { iocValue.setType(null); Set<Object> set = new HashSet<Object>(); set.addAll(paserCollection(element)); iocValue.setValue(set); } else { iocValue.setType(null); if (element.getFirstChild() != null) iocValue.setValue(element.getFirstChild().getTextContent()); } return iocValue; } protected List<IocValue> paserCollection(Element element) throws Throwable { List<IocValue> list = new ArrayList<IocValue>(); if (element.hasChildNodes()) { NodeList nodeList = element.getChildNodes(); for (int i = 0; i < nodeList.getLength(); i++) { Node node = nodeList.item(i); if (node instanceof Element) { list.add((IocValue) parseX((Element) node)); } } } return list; } protected Map<String, ?> paserMap(Element element) throws Throwable { Map<String, Object> map = new HashMap<String, Object>(); if (element.hasChildNodes()) { List<Element> elist = getChildNodesByTagName(element, ITEM_TAG); for (Element elementItem : elist) { String key = elementItem.getAttribute("key"); if (map.containsKey(key)) throw new IllegalArgumentException("key is not unique!"); NodeList list = elementItem.getChildNodes(); for (int j = 0; j < list.getLength(); j++) { if (list.item(j) instanceof Element) { map.put(key, parseX((Element) list.item(j))); break; } } if (!map.containsKey(key)) map.put(key, null); } } return map; } protected void parseEvents(Element beanElement, IocObject iocObject) { List<Element> elist = getChildNodesByTagName(beanElement, "events"); if (elist.size() > 0) { Element eventsElement = elist.get(0); IocEventSet iocEventSet = new IocEventSet(); elist = getChildNodesByTagName(eventsElement, "fetch"); if (elist.size() > 0) iocEventSet.setFetch(elist.get(0).getTextContent()); elist = getChildNodesByTagName(eventsElement, "create"); if (elist.size() > 0) iocEventSet.setCreate(elist.get(0).getTextContent()); elist = getChildNodesByTagName(eventsElement, "depose"); if (elist.size() > 0) iocEventSet.setDepose(elist.get(0).getTextContent()); if (iocEventSet.getCreate() == null) if (iocEventSet.getDepose() == null) if (iocEventSet.getFetch() == null) return; iocObject.setEvents(iocEventSet); } } protected void handleParent() { // 检查parentId是否都存在. for (String parentId : parentMap.values()) if (!iocMap.containsKey(parentId)) throw Lang.makeThrow("发现无效的parent=%s", parentId); // 检查循环依赖 List<String> parentList = new ArrayList<String>(); for (Entry<String, String> entry : parentMap.entrySet()) { if (!check(parentList, entry.getKey())) throw Lang.makeThrow("发现循环依赖! bean id=%s", entry.getKey()); parentList.clear(); } while (parentMap.size() != 0) { Iterator<Entry<String, String>> it = parentMap.entrySet().iterator(); while (it.hasNext()) { Entry<String, String> entry = it.next(); String beanId = entry.getKey(); String parentId = entry.getValue(); if (parentMap.get(parentId) == null) { IocObject newIocObject = Iocs.mergeWith(iocMap.get(beanId), iocMap.get(parentId)); iocMap.put(beanId, newIocObject); it.remove(); } } } } protected boolean check(List<String> parentList, String currentBeanId) { if (parentList.contains(currentBeanId)) return false; String parentBeanId = parentMap.get(currentBeanId); if (parentBeanId == null) return true; parentList.add(currentBeanId); return check(parentList, parentBeanId); } protected String getScanPatten() { return ".+[.]xml$"; } protected List<Element> getChildNodesByTagName(Element element, String tagName) { List<Element> list = new ArrayList<Element>(); NodeList nList = element.getElementsByTagName(tagName); if(nList.getLength() > 0) { for (int i = 0; i < nList.getLength(); i++) { Node node = nList.item(i); if(node.getParentNode().isSameNode(element) && node instanceof Element) list.add((Element) node); } } return list; } }