com.bstek.dorado.config.xml.XmlParserHelper.java Source code

Java tutorial

Introduction

Here is the source code for com.bstek.dorado.config.xml.XmlParserHelper.java

Source

/*
 * This file is part of Dorado 7.x (http://dorado7.bsdn.org).
 * 
 * Copyright (c) 2002-2012 BSTEK Corp. All rights reserved.
 * 
 * This file is dual-licensed under the AGPLv3 (http://www.gnu.org/licenses/agpl-3.0.html) 
 * and BSDN commercial (http://www.bsdn.org/licenses) licenses.
 * 
 * If you are unsure which license is appropriate for your use, please contact the sales department
 * at http://www.bstek.com/contact.
 */

package com.bstek.dorado.config.xml;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javassist.Modifier;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.ListableBeanFactory;
import org.w3c.dom.Node;

import com.bstek.dorado.annotation.ExpressionMode;
import com.bstek.dorado.annotation.XmlNode;
import com.bstek.dorado.annotation.XmlNodeWrapper;
import com.bstek.dorado.annotation.XmlProperty;
import com.bstek.dorado.annotation.XmlSubNode;
import com.bstek.dorado.common.event.ClientEventSupported;
import com.bstek.dorado.config.ParseContext;
import com.bstek.dorado.config.text.TextParser;
import com.bstek.dorado.config.text.TextParserHelper;
import com.bstek.dorado.core.bean.BeanFactoryUtils;
import com.bstek.dorado.core.bean.BeanWrapper;
import com.bstek.dorado.core.bean.Scope;
import com.bstek.dorado.util.clazz.ClassUtils;
import com.bstek.dorado.util.clazz.TypeInfo;

/**
 * @author Benny Bao (mailto:benny.bao@bstek.com)
 * @since 2011-11-12
 */
public class XmlParserHelper implements BeanFactoryAware {
    private static final String WILCARD = "*";
    private static final String SELF = "#self";

    private static final String DEFAULT_PARSER = "spring:dorado.prototype.objectParser";
    private static final String IGNORE_PARSER = "dorado.ignoreParser";
    private static final String UNSUPPORT_PARSER = "dorado.unsupportParser";
    private static final String COMPOSITE_PROPERTY_PARSER = "dorado.prototype.compositePropertyParser";
    private static final String CLASS_TYPE_PROPERTY_PARSER = "dorado.classTypePropertyParser";
    private static final String STRING_ARRAY_PROPERTY_PARSER = "dorado.stringArrayPropertyParser";
    private static final String DATA_PROPERTY_PARSER = "dorado.dataParser";
    private static final String STATIC_PROPERTY_PARSER = "dorado.staticPropertyParser";

    private static final String CLIENT_EVENT_PARSER = "dorado.clientEventParser";
    private static final String SUB_NODE_TO_PROPERTY_PARSER = "dorado.prototype.subNodeToPropertyParser";
    private static final String COLLECTION_TO_PROPERTY_PARSER = "dorado.prototype.collectionToPropertyParser";

    @SuppressWarnings("unchecked")
    private static List<XmlParserInfo> EMPTY_XML_PARSER_INFOS = Collections.EMPTY_LIST;

    public static class XmlParserInfo {
        private String path;
        private XmlParser parser;

        public XmlParserInfo(String path, XmlParser parser) {
            this.path = path;
            this.parser = parser;
        }

        public XmlParserInfo(String nodeName, String fixedProperties, XmlParser parser) {
            String path;
            if (StringUtils.isEmpty(fixedProperties)) {
                path = nodeName;
            } else {
                path = nodeName + '[' + fixedProperties + ']';
            }
            this.path = path;
            this.parser = parser;
        }

        public String getPath() {
            return path;
        }

        public XmlParser getParser() {
            return parser;
        }

    }

    protected static class MockParser implements XmlParser {
        private Class<?> beanType;
        private String wrapperParser;
        private String property;

        public MockParser(Class<?> beanType) {
            this.beanType = beanType;
        }

        public Class<?> getBeanType() {
            return beanType;
        }

        public String getWrapperParser() {
            return wrapperParser;
        }

        public void setWrapperParser(String wrapperParser) {
            this.wrapperParser = wrapperParser;
        }

        public String getProperty() {
            return property;
        }

        public void setProperty(String property) {
            this.property = property;
        }

        public Object parse(Node node, ParseContext context) throws Exception {
            throw new UnsupportedOperationException();
        }

    }

    protected static class Context {
        private Set<Class<?>> processingStack = new HashSet<Class<?>>();
        private Set<XmlParser> processingParsers = new HashSet<XmlParser>();

        public Set<Class<?>> getProcessingStack() {
            return processingStack;
        }

        public Set<XmlParser> getProcessingParsers() {
            return processingParsers;
        }

    }

    protected static class XmlNodeInfo {
        private Class<?> type;
        private List<Class<?>> sourceTypes;

        private String nodeName;
        private Set<String> implTypes = new HashSet<String>();
        private String definitionType;
        private boolean scopable;
        private boolean inheritable;
        private String parser;
        private Map<String, String> fixedProperties = new HashMap<String, String>();
        private Map<String, XmlProperty> properties = new HashMap<String, XmlProperty>();
        private Set<XmlSubNode> subNodes = new HashSet<XmlSubNode>();

        public XmlNodeInfo(Class<?> type) {
            this.type = type;
        }

        public Class<?> getType() {
            return type;
        }

        public void addSourceType(Class<?> sourceType) {
            if (sourceTypes == null) {
                sourceTypes = new ArrayList<Class<?>>();
            }
            sourceTypes.add(sourceType);
        }

        public List<Class<?>> getSourceTypes() {
            return sourceTypes;
        }

        public String getNodeName() {
            return nodeName;
        }

        public void setNodeName(String nodeName) {
            this.nodeName = nodeName;
        }

        public Set<String> getImplTypes() {
            return implTypes;
        }

        public String getDefinitionType() {
            return definitionType;
        }

        public void setDefinitionType(String definitionType) {
            this.definitionType = definitionType;
        }

        public boolean isScopable() {
            return scopable;
        }

        public void setScopable(boolean scopable) {
            this.scopable = scopable;
        }

        public boolean isInheritable() {
            return inheritable;
        }

        public void setInheritable(boolean inheritable) {
            this.inheritable = inheritable;
        }

        public String getParser() {
            return parser;
        }

        public void setParser(String parser) {
            this.parser = parser;
        }

        public Map<String, String> getFixedProperties() {
            return fixedProperties;
        }

        public String getFixedPropertiesExpression() {
            StringBuffer buf = new StringBuffer();
            for (Map.Entry<String, String> entry : fixedProperties.entrySet()) {
                if ((buf.length() > 0)) {
                    buf.append(';');
                }
                buf.append(entry.getKey()).append('=').append(entry.getValue());
            }
            return (buf.length() > 0) ? buf.toString() : null;
        }

        public Map<String, XmlProperty> getProperties() {
            return properties;
        }

        public Set<XmlSubNode> getSubNodes() {
            return subNodes;
        }
    }

    protected BeanFactory beanFactory;
    private TextParserHelper textParserHelper;
    private Set<ObjectParser> initializedObjectParsers = new HashSet<ObjectParser>();
    private Map<Class<?>, List<XmlParserInfo>> xmlParserInfoCache = new HashMap<Class<?>, List<XmlParserInfo>>();
    private Map<Class<?>, XmlNodeInfo> xmlNodeInfoCache = new HashMap<Class<?>, XmlNodeInfo>();

    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    public void setTextParserHelper(TextParserHelper textParserHelper) {
        this.textParserHelper = textParserHelper;
    }

    public List<XmlParserInfo> getXmlParserInfos(Class<?> beanType) throws Exception {
        synchronized (beanType) {
            if (xmlParserInfoCache.containsKey(beanType)) {
                return xmlParserInfoCache.get(beanType);
            }

            Context context = new Context();
            List<XmlParserInfo> xmlParserInfos = getSubNodeXmlParserInfosByPropertyType(context, beanType);
            for (XmlParserInfo xmlParserInfo : xmlParserInfos) {
                XmlParser parser = xmlParserInfo.getParser();
                if (parser instanceof DispatchableXmlParser) {
                    processMockParsers(context, (DispatchableXmlParser) parser);
                }
            }
            return (xmlParserInfos == null) ? EMPTY_XML_PARSER_INFOS : xmlParserInfos;
        }
    }

    public XmlParser getXmlParser(Class<?> beanType) throws Exception {
        List<XmlParserInfo> xmlParserInfos = getXmlParserInfos(beanType);
        if (xmlParserInfos != null && !xmlParserInfos.isEmpty()) {
            return xmlParserInfos.get(0).getParser();
        } else {
            return null;
        }
    }

    void initObjectParser(ObjectParser objectParser) throws Exception {
        initObjectParser(objectParser, null);
    }

    protected void initObjectParser(ObjectParser objectParser, Class<?> beanType) throws Exception {
        if (initializedObjectParsers.contains(objectParser)) {
            return;
        }

        if (beanType == null && !StringUtils.isEmpty(objectParser.getImpl())) {
            beanType = ClassUtils.forName(objectParser.getImpl());
        }
        if (beanType != null) {
            XmlNodeInfo xmlNodeInfo = getXmlNodeInfo(beanType);
            if (xmlNodeInfo != null || objectParser instanceof CompositePropertyParser) {
                Context context = new Context();
                initObjectParser(context, objectParser, xmlNodeInfo, beanType);
            }
        }
    }

    protected XmlNodeInfo getXmlNodeInfo(Class<?> type) {
        if (xmlNodeInfoCache.containsKey(type)) {
            return xmlNodeInfoCache.get(type);
        }

        XmlNodeInfo xmlNodeInfo = new XmlNodeInfo(type);
        doGetXmlNodeInfo(xmlNodeInfo, type);
        if (StringUtils.isEmpty(xmlNodeInfo.getNodeName()) || "#className".equals(xmlNodeInfo.getNodeName())) {
            xmlNodeInfo.setNodeName(type.getSimpleName());
        }
        if (xmlNodeInfo.getSourceTypes() == null) {
            xmlNodeInfo = null;
        }

        xmlNodeInfoCache.put(type, xmlNodeInfo);
        return xmlNodeInfo;
    }

    private void doGetXmlNodeInfo(XmlNodeInfo xmlNodeInfo, Class<?> type) {
        for (Class<?> i : type.getInterfaces()) {
            doGetXmlNodeInfo(xmlNodeInfo, i);
        }

        Class<?> superclass = type.getSuperclass();
        if (superclass != null && !superclass.equals(Object.class)) {
            doGetXmlNodeInfo(xmlNodeInfo, superclass);
        }

        collectXmlNodeInfo(xmlNodeInfo, type);
    }

    private void collectXmlNodeInfo(XmlNodeInfo xmlNodeInfo, Class<?> type) {
        XmlNode xmlNode = type.getAnnotation(XmlNode.class);
        if (xmlNode == null) {
            return;
        }
        xmlNodeInfo.addSourceType(type);

        if (StringUtils.isNotEmpty(xmlNode.nodeName())) {
            xmlNodeInfo.setNodeName(xmlNode.nodeName());
        }

        if (type.equals(xmlNodeInfo.getType())) {
            for (String implType : xmlNode.implTypes()) {
                if (StringUtils.isNotEmpty(implType)) {
                    xmlNodeInfo.getImplTypes().add(implType);
                }
            }
        }

        if (StringUtils.isNotEmpty(xmlNode.definitionType())) {
            xmlNodeInfo.setDefinitionType(xmlNode.definitionType());
        }
        if (!xmlNodeInfo.isScopable() && xmlNode.scopable()) {
            xmlNodeInfo.setScopable(true);
        }
        if (!xmlNodeInfo.isInheritable() && xmlNode.inheritable()) {
            xmlNodeInfo.setInheritable(true);
        }
        if (StringUtils.isNotEmpty(xmlNode.parser())) {
            xmlNodeInfo.setParser(xmlNode.parser());
        }
        if (StringUtils.isNotEmpty(xmlNode.fixedProperties())) {
            Map<String, String> fixedProperties = xmlNodeInfo.getFixedProperties();
            for (String fixedProperty : StringUtils.split(xmlNode.fixedProperties(), ",;")) {
                int i = fixedProperty.indexOf('=');
                if (i > 0) {
                    String property = fixedProperty.substring(0, i);
                    fixedProperties.put(property, fixedProperty.substring(i + 1));
                }
            }
        }

        Map<String, XmlProperty> properties = xmlNodeInfo.getProperties();
        XmlProperty[] annotationProperties = xmlNode.properties();
        boolean hasPropertyAnnotation = false;
        if (annotationProperties.length == 1) {
            XmlProperty xmlProperty = annotationProperties[0];
            hasPropertyAnnotation = StringUtils.isNotEmpty(xmlProperty.propertyName())
                    || StringUtils.isNotEmpty(xmlProperty.propertyType())
                    || StringUtils.isNotEmpty(xmlProperty.parser());
        } else if (annotationProperties.length > 1) {
            hasPropertyAnnotation = true;
        }

        if (hasPropertyAnnotation) {
            for (XmlProperty xmlProperty : annotationProperties) {
                if (StringUtils.isEmpty(xmlProperty.propertyName())) {
                    throw new IllegalArgumentException(
                            "@XmlProperty.propertyName undefined. [" + type.getName() + "]");
                }
                for (String property : StringUtils.split(xmlProperty.propertyName(), ",;")) {
                    properties.put(property, xmlProperty);
                }
            }
        }

        Set<XmlSubNode> subNodes = xmlNodeInfo.getSubNodes();
        XmlSubNode[] annotationSubNodes = xmlNode.subNodes();
        boolean hasSubNodeAnnotation = (annotationSubNodes.length > 0);
        if (annotationSubNodes.length == 1) {
            XmlSubNode xmlSubNode = annotationSubNodes[0];
            hasSubNodeAnnotation = StringUtils.isNotEmpty(xmlSubNode.propertyName())
                    || StringUtils.isNotEmpty(xmlSubNode.nodeName())
                    || StringUtils.isNotEmpty(xmlSubNode.propertyType());
        }
        if (hasSubNodeAnnotation) {
            for (XmlSubNode xmlSubNode : annotationSubNodes) {
                if (!(StringUtils.isNotEmpty(xmlSubNode.propertyType())
                        || (StringUtils.isNotEmpty(xmlSubNode.nodeName())
                                && StringUtils.isNotEmpty(xmlSubNode.parser())))) {
                    throw new IllegalArgumentException(
                            "Neither @XmlSubNode.propertyType nor @XmlSubNode.nodeName/@XmlSubNode.parser undefined. ["
                                    + type.getName() + "]");
                }
                subNodes.add(xmlSubNode);
            }
        }
    }

    private void processMockParsers(Context context, DispatchableXmlParser parentParser) throws Exception {
        if (context.getProcessingParsers().contains(parentParser)) {
            return;
        }
        context.getProcessingParsers().add(parentParser);

        boolean hasMockParser = false;
        Map<String, XmlParser> subParsers = parentParser.getSubParsers();
        for (XmlParser parser : subParsers.values()) {
            if (parser instanceof MockParser) {
                hasMockParser = true;
                break;
            }
        }

        if (hasMockParser) {
            Map<String, XmlParser> newParserMap = new LinkedHashMap<String, XmlParser>();
            for (Map.Entry<String, XmlParser> entry : subParsers.entrySet()) {
                String path = entry.getKey();
                XmlParser parser = entry.getValue();
                if (parser instanceof MockParser) {
                    MockParser mockParser = (MockParser) parser;
                    List<XmlParserInfo> xmlParserInfos = xmlParserInfoCache.get(mockParser.getBeanType());
                    if (xmlParserInfos != null) {
                        String wrapperParserConfig = mockParser.getWrapperParser();
                        if (StringUtils.isNotEmpty(wrapperParserConfig)) {
                            for (XmlParserInfo xmlParserInfo : xmlParserInfos) {
                                DispatchableXmlParser wrapperParser;
                                wrapperParser = beanFactory.getBean(wrapperParserConfig,
                                        DispatchableXmlParser.class);
                                BeanUtils.setProperty(wrapperParser, "property", mockParser.getProperty());
                                wrapperParser.registerSubParser(SELF, xmlParserInfo.getParser());

                                int i = path.lastIndexOf('/');
                                if (i > 0) {
                                    path = path.substring(0, i + 1) + xmlParserInfo.getPath();
                                } else {
                                    path = xmlParserInfo.getPath();
                                }

                                newParserMap.put(path, wrapperParser);
                            }
                        } else {
                            for (XmlParserInfo xmlParserInfo : xmlParserInfos) {
                                newParserMap.put(xmlParserInfo.getPath(), xmlParserInfo.getParser());
                            }
                        }
                    }
                } else {
                    newParserMap.put(path, parser);
                }
            }

            subParsers.clear();
            subParsers.putAll(newParserMap);
        }

        for (XmlParser parser : subParsers.values()) {
            if (parser instanceof DispatchableXmlParser) {
                processMockParsers(context, (DispatchableXmlParser) parser);
            }
        }
    }

    private List<XmlParserInfo> doGetXmlParserInfos(Context context, Class<?> beanType) throws Exception {
        XmlNodeInfo xmlNodeInfo = getXmlNodeInfo(beanType);
        List<XmlParserInfo> xmlParserInfos = null;
        if (xmlNodeInfo != null) {
            xmlParserInfos = doGetXmlParserInfos(context, beanType, xmlNodeInfo);
        }
        if (xmlParserInfos == null) {
            xmlParserInfos = EMPTY_XML_PARSER_INFOS;
        }
        return xmlParserInfos;
    }

    private List<XmlParserInfo> doGetXmlParserInfos(Context context, Class<?> beanType, XmlNodeInfo xmlNodeInfo)
            throws Exception {
        Set<String> implTypes = xmlNodeInfo.getImplTypes();
        if (implTypes.isEmpty()) {
            return doGetXmlParserInfosByBeanType(context, beanType, xmlNodeInfo);
        } else {
            List<XmlParserInfo> xmlParserInfos = new ArrayList<XmlParserInfo>();
            for (String implType : implTypes) {
                if (StringUtils.isNotEmpty(implType)) {
                    collectConcereteXmlParsersByImplExpression(context, beanType, implType, xmlParserInfos, null);
                }
            }
            return xmlParserInfos;
        }
    }

    private List<XmlParserInfo> doGetXmlParserInfosByBeanType(Context context, Class<?> beanType,
            XmlNodeInfo xmlNodeInfo) throws Exception {
        if (xmlParserInfoCache.containsKey(beanType)) {
            return xmlParserInfoCache.get(beanType);
        }

        if (context.getProcessingStack().contains(beanType)) {
            MockParser mockParser = new MockParser(beanType);
            List<XmlParserInfo> xmlParserInfos = new ArrayList<XmlParserInfo>();
            xmlParserInfos.add(new XmlParserInfo(ObjectUtils.identityToString(mockParser), mockParser));
            return xmlParserInfos;
        }

        context.getProcessingStack().add(beanType);
        try {
            String nodeName = xmlNodeInfo.getNodeName();
            if (StringUtils.isEmpty(nodeName)) {
                nodeName = beanType.getSimpleName();
            }

            String parserExpression = xmlNodeInfo.getParser();
            if (StringUtils.isEmpty(parserExpression)) {
                parserExpression = DEFAULT_PARSER;
            }

            BeanWrapper beanWrapper = BeanFactoryUtils.getBean(parserExpression, Scope.instant);
            XmlParser parser = (XmlParser) beanWrapper.getBean();
            if (parser instanceof ObjectParser) {
                ObjectParser objectParser = (ObjectParser) parser;
                initObjectParser(context, objectParser, xmlNodeInfo, beanType);
            }

            List<XmlParserInfo> xmlParserInfos = new ArrayList<XmlParserInfo>();
            xmlParserInfos.add(new XmlParserInfo(nodeName, xmlNodeInfo.getFixedPropertiesExpression(), parser));
            xmlParserInfoCache.put(beanType, xmlParserInfos);
            return xmlParserInfos;
        } finally {
            context.getProcessingStack().remove(beanType);
        }
    }

    private final void initObjectParser(Context context, ObjectParser objectParser, XmlNodeInfo xmlNodeInfo,
            Class<?> beanType) throws Exception {
        if (initializedObjectParsers.contains(objectParser)) {
            return;
        }
        initializedObjectParsers.add(objectParser);
        doInitObjectParser(context, objectParser, xmlNodeInfo, beanType);
    }

    protected void doInitObjectParser(Context context, ObjectParser objectParser, XmlNodeInfo xmlNodeInfo,
            Class<?> beanType) throws Exception {
        objectParser.setAnnotationOwnerType(beanType);
        if (!Modifier.isAbstract(beanType.getModifiers())) {
            objectParser.setImpl(beanType.getName());
        }

        if (xmlNodeInfo != null) {
            if (StringUtils.isNotEmpty(xmlNodeInfo.getDefinitionType())) {
                objectParser.setDefinitionType(xmlNodeInfo.getDefinitionType());
            }

            Map<String, XmlParser> propertyParsers = objectParser.getPropertyParsers();
            Map<String, XmlParser> subParsers = objectParser.getSubParsers();

            if (!(objectParser instanceof CompositePropertyParser)) {
                boolean inheritable = objectParser.isInheritable() || xmlNodeInfo.isInheritable();
                objectParser.setInheritable(inheritable);
                if (inheritable) {
                    if (propertyParsers.get("parent") == null) {
                        objectParser.registerPropertyParser("parent",
                                beanFactory.getBean(IGNORE_PARSER, XmlParser.class));
                    }
                }

                boolean scopable = objectParser.isScopable() || xmlNodeInfo.isScopable();
                objectParser.setScopable(scopable);
                if (scopable) {
                    if (propertyParsers.get("scope") == null) {
                        objectParser.registerPropertyParser("scope",
                                beanFactory.getBean(IGNORE_PARSER, XmlParser.class));
                    }
                }

                for (String fixedProperty : xmlNodeInfo.getFixedProperties().keySet()) {
                    if (propertyParsers.get(fixedProperty) == null) {
                        objectParser.registerPropertyParser(fixedProperty,
                                beanFactory.getBean(IGNORE_PARSER, XmlParser.class));
                    }
                }
            }

            for (XmlSubNode xmlSubNode : xmlNodeInfo.getSubNodes()) {
                if (StringUtils.isNotEmpty(xmlSubNode.propertyType())) {
                    List<XmlParserInfo> xmlParserInfos = getSubNodeXmlParserInfos(context, beanType,
                            xmlSubNode.propertyName(), null, xmlSubNode);
                    if (xmlParserInfos != null) {
                        for (XmlParserInfo xmlParserInfo : xmlParserInfos) {
                            objectParser.registerSubParser(xmlParserInfo.getPath(), xmlParserInfo.getParser());
                        }
                    }
                } else if (StringUtils.isNotEmpty(xmlSubNode.nodeName())
                        && StringUtils.isNotEmpty(xmlSubNode.parser())) {
                    BeanWrapper beanWrapper = BeanFactoryUtils.getBean(xmlSubNode.parser(), Scope.instant);
                    objectParser.registerSubParser(xmlSubNode.nodeName(), (XmlParser) beanWrapper.getBean());
                }
            }

            for (Map.Entry<String, XmlProperty> entry : xmlNodeInfo.getProperties().entrySet()) {
                XmlProperty xmlProperty = entry.getValue();
                XmlParserInfo xmlParserInfo = getPropertyXmlParserInfo(context, beanType,
                        xmlProperty.propertyName(), null, xmlProperty);
                if (xmlParserInfo != null) {
                    objectParser.registerPropertyParser(xmlParserInfo.getPath(), xmlParserInfo.getParser());
                }
            }

            if (ClientEventSupported.class.isAssignableFrom(beanType) && subParsers.get("ClientEvent") == null) {
                objectParser.registerSubParser("ClientEvent",
                        beanFactory.getBean(CLIENT_EVENT_PARSER, XmlParser.class));
            }
        }

        PropertyDescriptor[] propertyDescriptors = PropertyUtils.getPropertyDescriptors(beanType);
        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
            String propertyName = propertyDescriptor.getName();
            if ("class".equals(propertyName)) {
                continue;
            }

            Method readMethod = propertyDescriptor.getReadMethod();
            if (readMethod == null) {
                continue;
            }
            if (readMethod.getDeclaringClass() != beanType) {
                try {
                    readMethod = beanType.getMethod(readMethod.getName(), readMethod.getParameterTypes());
                } catch (NoSuchMethodException e) {
                    // do nothing
                }
            }

            TypeInfo typeInfo;
            Class<?> propertyType = propertyDescriptor.getPropertyType();
            if (Collection.class.isAssignableFrom(propertyType)) {
                typeInfo = TypeInfo.parse((ParameterizedType) readMethod.getGenericReturnType(), true);
                propertyType = typeInfo.getType();
            } else {
                typeInfo = new TypeInfo(propertyType, false);
            }

            XmlSubNode xmlSubNode = readMethod.getAnnotation(XmlSubNode.class);
            if (xmlSubNode != null) {
                if (StringUtils.isNotEmpty(xmlSubNode.propertyName())) {
                    throw new IllegalArgumentException("@XmlSubNode.propertyName should be empty. ["
                            + beanType.getName() + '#' + propertyName + "]");
                }

                List<XmlParserInfo> xmlParserInfos = getSubNodeXmlParserInfos(context, beanType, propertyName,
                        typeInfo, xmlSubNode);
                if (xmlParserInfos != null) {
                    for (XmlParserInfo xmlParserInfo : xmlParserInfos) {
                        objectParser.registerSubParser(xmlParserInfo.getPath(), xmlParserInfo.getParser());
                    }
                }
            } else {
                XmlProperty xmlProperty = readMethod.getAnnotation(XmlProperty.class);
                if (xmlProperty != null && StringUtils.isNotEmpty(xmlProperty.propertyName())) {
                    throw new IllegalArgumentException("@XmlProperty.propertyName should be empty. ["
                            + beanType.getName() + '#' + propertyName + "]");
                }

                XmlParserInfo xmlParserInfo = getPropertyXmlParserInfo(context, beanType, propertyName, typeInfo,
                        xmlProperty);
                if (xmlParserInfo != null) {
                    XmlParser parser = xmlParserInfo.getParser();
                    if (parser instanceof TextPropertyParser) {
                        TextPropertyParser textPropertyParser = (TextPropertyParser) parser;
                        if (textPropertyParser.getTextParser() == null) {
                            TextParser textParser = textParserHelper.getTextParser(propertyType);
                            textPropertyParser.setTextParser(textParser);
                        }
                    }
                    objectParser.registerPropertyParser(xmlParserInfo.getPath(), parser);
                }
            }
        }

        if (objectParser instanceof ObjectParserInitializationAware) {
            ((ObjectParserInitializationAware) objectParser).postObjectParserInitialized(objectParser);
        }

        Map<String, XmlParserHelperListener> listenerMap = ((ListableBeanFactory) beanFactory)
                .getBeansOfType(XmlParserHelperListener.class);
        for (XmlParserHelperListener listener : listenerMap.values()) {
            listener.onInitParser(this, objectParser, beanType);
        }
    }

    protected XmlParserInfo getPropertyXmlParserInfo(Context context, Class<?> beanType, String propertyName,
            TypeInfo typeInfo, XmlProperty xmlProperty) throws Exception {
        XmlParser propertyParser = null;

        Class<?> propertyType = (typeInfo != null) ? typeInfo.getType() : null;
        if (xmlProperty != null) {
            if (StringUtils.isNotEmpty(xmlProperty.propertyType())) {
                typeInfo = TypeInfo.parse(xmlProperty.propertyType());
                propertyType = typeInfo.getType();
            }
            if (StringUtils.isNotEmpty(xmlProperty.propertyType())) {
                propertyType = ClassUtils.forName(xmlProperty.propertyType());
            }

            if (xmlProperty.unsupported()) {
                propertyParser = beanFactory.getBean(UNSUPPORT_PARSER, XmlParser.class);
            } else if (xmlProperty.ignored()) {
                propertyParser = beanFactory.getBean(IGNORE_PARSER, XmlParser.class);
            } else if (StringUtils.isNotEmpty(xmlProperty.parser())) {
                propertyParser = (XmlParser) BeanFactoryUtils.getBean(xmlProperty.parser());
            } else {
                if (xmlProperty.composite()) {
                    CompositePropertyParser compositePropertyParser = beanFactory.getBean(COMPOSITE_PROPERTY_PARSER,
                            CompositePropertyParser.class);

                    compositePropertyParser.setImpl(propertyType.getName());
                    compositePropertyParser.setOpen(Map.class.isAssignableFrom(propertyType));
                    if (!compositePropertyParser.isOpen()) {
                        initObjectParser(compositePropertyParser);
                    }

                    propertyParser = compositePropertyParser;
                } else {
                    ExpressionMode expressionMode = xmlProperty.expressionMode();
                    if (ExpressionMode.NORMAL.equals(expressionMode)) {
                        propertyParser = beanFactory.getBean(STATIC_PROPERTY_PARSER, XmlParser.class);
                    }
                }
            }
        }

        if (propertyParser == null && propertyType != null) {
            if (propertyType.equals(Class.class)) {
                propertyParser = beanFactory.getBean(CLASS_TYPE_PROPERTY_PARSER, XmlParser.class);
            } else if (propertyType.isArray() && propertyType.getComponentType().equals(String.class)) {
                propertyParser = beanFactory.getBean(STRING_ARRAY_PROPERTY_PARSER, XmlParser.class);
            } else if (propertyType.equals(Object.class)) {
                propertyParser = beanFactory.getBean(DATA_PROPERTY_PARSER, XmlParser.class);
            }
        }

        if (propertyParser != null) {
            return new XmlParserInfo(propertyName, propertyParser);
        } else {
            return null;
        }
    }

    protected List<XmlParserInfo> getSubNodeXmlParserInfos(Context context, Class<?> beanType, String propertyName,
            TypeInfo typeInfo, XmlSubNode xmlSubNode) throws Exception {
        List<XmlParserInfo> xmlParserInfos;

        if (StringUtils.isNotEmpty(xmlSubNode.propertyType())) {
            typeInfo = TypeInfo.parse(xmlSubNode.propertyType());
        }
        Class<?> propertyType = typeInfo.getType();

        xmlParserInfos = new ArrayList<XmlParserInfo>();
        String nodeName = xmlSubNode.nodeName();
        if (StringUtils.isNotEmpty(nodeName) && StringUtils.isNotEmpty(xmlSubNode.parser())) {
            XmlParser subParser = (XmlParser) BeanFactoryUtils.getBean(xmlSubNode.parser());
            XmlParserInfo parserInfo = new XmlParserInfo(nodeName, subParser);
            xmlParserInfos.add(parserInfo);
        } else {
            String[] implTypes = xmlSubNode.implTypes();
            Set<Class<?>> blackTypes = null;

            for (String implType : implTypes) {
                if (StringUtils.isNotEmpty(implType)) {
                    if (blackTypes == null) {
                        blackTypes = new HashSet<Class<?>>();
                        blackTypes.add(beanType);
                    }

                    collectConcereteXmlParsersByImplExpression(context, propertyType, implType, xmlParserInfos,
                            blackTypes);
                }
            }
            List<XmlParserInfo> xpis = getSubNodeXmlParserInfosByPropertyType(context, propertyType);
            if (xpis != null) {
                xmlParserInfos.addAll(xpis);
            }

            if (StringUtils.isNotEmpty(nodeName) && !nodeName.contains(DispatchableXmlParser.WILDCARD)
                    && !xmlParserInfos.isEmpty()) {
                if (xmlParserInfos.size() == 1) {
                    XmlParserInfo parserInfo = xmlParserInfos.get(0);
                    xmlParserInfos.set(0, new XmlParserInfo(nodeName, parserInfo.getParser()));
                } else {
                    throw new IllegalArgumentException("Assign nodeName for mora than one parse. ["
                            + beanType.getName() + "." + propertyName + "], [" + nodeName + "].");
                }
            }
        }

        XmlNodeWrapper wrapper = xmlSubNode.wrapper();
        if (!xmlParserInfos.isEmpty() && StringUtils.isNotEmpty(propertyName)) {
            int size = xmlParserInfos.size();
            for (int i = 0; i < size; i++) {
                XmlParserInfo xmlParserInfo = xmlParserInfos.get(i);
                XmlParser subParser = xmlParserInfo.getParser();
                String path = xmlParserInfo.getPath();
                if (StringUtils.isNotEmpty(wrapper.nodeName())) {
                    path = wrapper.nodeName() + '/' + path;
                }

                if (!xmlSubNode.resultProcessed()) {
                    if (subParser instanceof MockParser) {
                        MockParser mockParser = (MockParser) subParser;
                        mockParser.setWrapperParser((typeInfo.isAggregated()) ? COLLECTION_TO_PROPERTY_PARSER
                                : SUB_NODE_TO_PROPERTY_PARSER);
                        mockParser.setProperty(propertyName);
                        XmlParserInfo mockParserInfo = new XmlParserInfo(path, mockParser);
                        xmlParserInfos.set(i, mockParserInfo);
                    } else {
                        DispatchableXmlParser wrapperParser;
                        if (typeInfo.isAggregated()) {
                            wrapperParser = (DispatchableXmlParser) beanFactory
                                    .getBean(COLLECTION_TO_PROPERTY_PARSER);
                        } else {
                            wrapperParser = (DispatchableXmlParser) beanFactory
                                    .getBean(SUB_NODE_TO_PROPERTY_PARSER);
                        }
                        BeanUtils.setProperty(wrapperParser, "property", propertyName);

                        wrapperParser.registerSubParser(SELF, subParser);
                        XmlParserInfo wrapperParserInfo = new XmlParserInfo(path, wrapperParser);
                        xmlParserInfos.set(i, wrapperParserInfo);
                    }
                }
            }
        }
        return xmlParserInfos;
    }

    protected List<XmlParserInfo> getSubNodeXmlParserInfosByPropertyType(Context context, Class<?> propertyType)
            throws Exception {
        return doGetXmlParserInfos(context, propertyType);
    }

    private void collectConcereteXmlParsersByImplExpression(Context context, Class<?> targetType,
            String implExpression, List<XmlParserInfo> xmlParserInfos, Set<Class<?>> blackTypes) throws Exception {
        if (implExpression.indexOf(WILCARD) >= 0) {
            Set<Class<?>> implTypes = ClassUtils.findClassTypes(implExpression, targetType);
            for (Class<?> implType : implTypes) {
                if (blackTypes == null || !blackTypes.contains(implType)) {
                    collectConcereteXmlParsersByClassName(context, targetType, implType, xmlParserInfos, false);
                }
            }
        } else {
            try {
                Class<?> implType = ClassUtils.forName(implExpression);
                if (implType != null && (blackTypes == null || !blackTypes.contains(implType))) {
                    collectConcereteXmlParsersByClassName(context, targetType, implType, xmlParserInfos, true);
                }
            } catch (ClassNotFoundException e) {
                // do nothing
            }
        }
    }

    private void collectConcereteXmlParsersByClassName(Context context, Class<?> targetType, Class<?> implType,
            List<XmlParserInfo> xmlParserInfos, boolean checkType) throws Exception {
        if (Modifier.isAbstract(implType.getModifiers()) || !targetType.isAssignableFrom(implType)) {
            if (checkType) {
                throw new IllegalArgumentException(
                        implType.getName() + " is not a sub type of " + targetType.getName() + ".");
            }
            return;
        }

        XmlNodeInfo concereteXmlNodeInfo = getXmlNodeInfo(implType);
        if (concereteXmlNodeInfo != null) {
            for (XmlParserInfo xmlParserInfo : doGetXmlParserInfosByBeanType(context, implType,
                    concereteXmlNodeInfo)) {
                xmlParserInfos.add(xmlParserInfo);
            }
        }
    }
}