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

Java tutorial

Introduction

Here is the source code for com.bstek.dorado.config.xml.DispatchableXmlParser.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.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.Set;

import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

import com.bstek.dorado.config.ConfigUtils;
import com.bstek.dorado.config.ParseContext;
import com.bstek.dorado.core.el.ExpressionHandler;
import com.bstek.dorado.util.Assert;
import com.bstek.dorado.util.PathUtils;
import com.bstek.dorado.util.proxy.ChildrenMapSupport;
import com.bstek.dorado.util.xml.DomUtils;

/**
 * XML?<br>
 * ??XML?????
 * 
 * @author Benny Bao (mailto:benny.bao@bstek.com)
 * @since Feb 20, 2007
 */
public class DispatchableXmlParser implements XmlParser {
    public static final String SELF = "#self";
    public static final char SUB_PARSER_PATH_SEPERATOR = '/';

    /**
     * ???<br>
     * ????????{@link #WILDCARD}?
     */
    public static final String WILDCARD = "*";

    private Map<String, XmlParser> propertyParsers = new LinkedHashMap<String, XmlParser>();
    private SubParserMap subParsers = new SubParserMap();

    /**
     * EL??
     */
    protected ExpressionHandler getExpressionHandler() {
        return null;
    }

    /**
     * ????
     * 
     * @param constraint
     *            ?????null{@link #WILDCARD}
     *            ??
     * @param parser
     *            ?
     */
    public void registerPropertyParser(String constraint, XmlParser parser) {
        Assert.notNull(parser, "[parser] is required");
        propertyParsers.put(constraint, parser);
    }

    /**
     * ?Map???
     * 
     * @return ?Map?
     */
    public Map<String, XmlParser> getPropertyParsers() {
        return propertyParsers;
    }

    /**
     * ?????
     * 
     * @param constraint
     *            ?? ??????WILDCARD??
     */
    protected XmlParser findPropertyParser(String constraint) {
        constraint = StringUtils.defaultString(constraint, WILDCARD);
        XmlParser parser = propertyParsers.get(constraint);
        if (parser == null && !WILDCARD.equals(constraint)) {
            parser = propertyParsers.get(WILDCARD);
        }
        return parser;
    }

    /**
     * ?????
     * 
     * @param constraint
     *            ??????nullWILDCARD??
     * @param parser
     *            ??
     */
    public void registerSubParser(String constraint, XmlParser parser) {
        Assert.notNull(parser, "[parser] is required");
        subParsers.put(constraint, parser);
    }

    /**
     * ?? Map????
     * 
     * @return ??Map?
     */
    public Map<String, XmlParser> getSubParsers() {
        return subParsers;
    }

    /**
     * ??????
     * 
     * @param constraint
     *            ?? ??????WILDCARD??
     */
    protected XmlParser findSubParser(String constraint) {
        constraint = StringUtils.defaultString(constraint, WILDCARD);
        XmlParser parser = subParsers.get(constraint);
        if (parser == null && !WILDCARD.equals(constraint)) {
            parser = subParsers.get(WILDCARD);
        }
        return parser;
    }

    public final Object parse(Node node, ParseContext context) throws Exception {
        if (!subParsers.cacheBuilded) {
            subParsers.buildCache();
        }

        return doParse(node, context);
    }

    protected Object doParse(Node node, ParseContext context) throws Exception {
        if (node instanceof Element) {
            return dispatchChildElements((Element) node, context);
        } else {
            return null;
        }
    }

    private boolean isTagElement(Element element) {
        String nodeName = element.getNodeName();
        return (XmlConstants.PROPERTY.equals(nodeName) || XmlConstants.GROUP_START.equals(nodeName)
                || XmlConstants.GROUP_END.equals(nodeName) || XmlConstants.IMPORT.equals(nodeName)
                || XmlConstants.PLACE_HOLDER.equals(nodeName) || XmlConstants.PLACE_HOLDER_START.equals(nodeName)
                || XmlConstants.PLACE_HOLDER_END.equals(nodeName));
    }

    /**
     * ??????????List?
     * 
     * @param element
     *            ??XML
     * @param context
     *            ?
     * @return ?
     * @throws Exception
     */
    protected List<?> dispatchChildElements(Element element, ParseContext context) throws Exception {
        List<Object> results = new ArrayList<Object>();
        for (Element childElement : DomUtils.getChildElements(element)) {
            if (!isTagElement(childElement)) {
                Object value = dispatchElement(null, childElement, context);
                if (value != ConfigUtils.IGNORE_VALUE) {
                    results.add(value);
                }
            }
        }

        if (subParsers.hasDeepParser) {
            Set<String> parsedPaths = new HashSet<String>();
            for (Map.Entry<String, ParserInfo> entry : subParsers.subParserInfoMap.entrySet()) {
                String path = entry.getKey();
                ParserInfo parserInfo = entry.getValue();
                XmlParser subParser = parserInfo.getParser();
                PathSection[] pathSections = parserInfo.getPathSections();
                if (pathSections.length > 1) {
                    dispatchChildElementsWithConditionalPath(results, parsedPaths, pathSections, 0, null, element,
                            context);
                } else if (SELF.equals(path)) {
                    Object value = subParser.parse(element, context);
                    if (value != ConfigUtils.IGNORE_VALUE) {
                        results.add(value);
                    }
                }
            }
        } else {
            XmlParser subParser = subParsers.get(SELF);
            if (subParser != null) {
                Object value = subParser.parse(element, context);
                if (value != ConfigUtils.IGNORE_VALUE) {
                    results.add(value);
                }
            }
        }
        return results;
    }

    private void dispatchChildElementsWithConditionalPath(List<Object> results, Set<String> parsedPaths,
            PathSection[] pathSections, int deepth, String pathPrefix, Element element, ParseContext context)
            throws Exception {
        if (deepth < pathSections.length - 1) {
            String root = pathSections[deepth].getNodeName();
            String subPathPrefix = PathUtils.concatPath(pathPrefix, root);
            if (!parsedPaths.contains(subPathPrefix)) {
                parsedPaths.add(subPathPrefix);
                Element childRootNode = DomUtils.getChildByTagName(element, root);
                if (childRootNode != null) {
                    dispatchChildElementsWithConditionalPath(results, parsedPaths, pathSections, deepth + 1,
                            subPathPrefix, childRootNode, context);
                }
            }
        } else {
            for (Element childElement : DomUtils.getChildElements(element)) {
                if (!isTagElement(childElement)) {
                    Object value = dispatchElement(pathPrefix, childElement, context);
                    if (value != ConfigUtils.IGNORE_VALUE) {
                        results.add(value);
                    }
                }
            }
        }
    }

    /**
     * ?XML?????
     * 
     * @param element
     *            XML
     * @param context
     *            ?
     * @return ?
     * @throws Exception
     */
    protected Object dispatchElement(String pathPrefix, Element child, ParseContext context) throws Exception {
        String path = PathUtils.concatPath(pathPrefix, child.getNodeName());
        XmlParser parser = subParsers.get(path);
        if (parser != null) {
            return parser.parse(child, context);
        } else {
            if (subParsers.hasConditionalParser) {
                for (Map.Entry<String, ParserInfo> entry : subParsers.subParserInfoMap.entrySet()) {
                    String subParserPath = entry.getKey();
                    ParserInfo parserInfo = entry.getValue();
                    Map<String, String> fixedProperties = parserInfo.getFixedProperties();

                    if (fixedProperties == null || !subParserPath.startsWith(path + '[')) {
                        continue;
                    }

                    boolean according = true;
                    for (Map.Entry<String, String> propEntry : fixedProperties.entrySet()) {
                        if (!ObjectUtils.equals(child.getAttribute(propEntry.getKey()), propEntry.getValue())) {
                            according = false;
                            break;
                        }
                    }

                    if (according) {
                        parser = parserInfo.getParser();
                        return parser.parse(child, context);
                    }
                }
            }

            parser = subParsers.get(PathUtils.concatPath(pathPrefix, WILDCARD));
            if (parser != null) {
                return parser.parse(child, context);
            } else {
                return ConfigUtils.IGNORE_VALUE;
            }
        }
    }

    /**
     * ??<br>
     * XML(Attribute)???
     * <code><Property name="xxx">XXXX</Property></code>
     * ??
     * 
     * @param element
     *            XML
     * @param context
     *            ?
     * @return ?Map???
     *         ??????
     * @throws Exception
     */
    protected Map<String, Object> parseProperties(Element element, ParseContext context) throws Exception {
        Map<String, Object> properties = new HashMap<String, Object>();

        for (Element propertyElement : DomUtils.getChildrenByTagName(element, XmlConstants.PROPERTY)) {
            String name = propertyElement.getAttribute(XmlConstants.ATTRIBUTE_NAME);
            if (StringUtils.isNotEmpty(name)) {
                properties.put(name, propertyElement);
            }
        }

        NamedNodeMap attributes = element.getAttributes();
        int attributeNum = attributes.getLength();
        for (int i = 0; i < attributeNum; i++) {
            Node node = attributes.item(i);
            String property = node.getNodeName();
            properties.put(property, node);
        }

        for (Iterator<Map.Entry<String, Object>> it = properties.entrySet().iterator(); it.hasNext();) {
            Map.Entry<String, Object> entry = it.next();
            String property = entry.getKey();
            Node node = (Node) entry.getValue();
            Object value = parseProperty(property, node, context);
            if (value != ConfigUtils.IGNORE_VALUE) {
                entry.setValue(value);
            } else {
                it.remove();
            }
        }
        return properties;
    }

    /**
     * ?
     * 
     * @param property
     *            ??
     * @param node
     *            XML
     * @param context
     *            ?
     * @return ?
     * @throws Exception
     */
    protected Object parseProperty(String property, Node node, ParseContext context) throws Exception {
        Object value;
        XmlParser propertyParser = findPropertyParser(property);
        if (propertyParser != null) {
            value = propertyParser.parse(node, context);
        } else {
            value = ConfigUtils.IGNORE_VALUE;
        }
        return value;
    }

}

class PathSection {
    private String nodeName;
    private Map<String, String> fixedProperties;

    public static PathSection parse(String expression) {
        return new PathSection(expression);
    }

    private PathSection(String expression) {
        if (expression.indexOf('[') > 0) {
            StringBuffer nodeNameBuf = new StringBuffer(), fixedProperty = new StringBuffer(),
                    fixedPropertyValue = new StringBuffer();
            int len = expression.length();
            boolean inBracket = false, beforeEquals = true;
            for (int i = 0; i < len; i++) {
                char c = expression.charAt(i);
                if (!inBracket) {
                    if (c == '[') {
                        inBracket = true;
                    } else {
                        nodeNameBuf.append(c);
                    }
                } else {
                    if (beforeEquals) {
                        fixedProperty.append(c);
                    } else if (c == '=') {
                        beforeEquals = false;
                    } else if (c == ';' || c == ',' || c == ']' && i == len - 1) {
                        beforeEquals = true;
                        if (fixedProperties == null) {
                            fixedProperties = new HashMap<String, String>();
                        }
                        fixedProperties.put(fixedProperty.toString(), fixedPropertyValue.toString());
                        fixedProperty.setLength(0);
                        fixedPropertyValue.setLength(0);
                    } else {
                        fixedPropertyValue.append(c);
                    }
                }
            }
        } else {
            nodeName = expression;
        }
    }

    public String getNodeName() {
        return nodeName;
    }

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

class ParserInfo {
    private XmlParser parser;
    private PathSection[] pathSections;
    private Map<String, String> fixedProperties;

    public ParserInfo(XmlParser parser, PathSection[] pathSections) {
        this.parser = parser;
        this.pathSections = pathSections;
        fixedProperties = pathSections[pathSections.length - 1].getFixedProperties();
    }

    public XmlParser getParser() {
        return parser;
    }

    public PathSection[] getPathSections() {
        return pathSections;
    }

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

class SubParserMap extends ChildrenMapSupport<String, XmlParser> {
    public boolean cacheBuilded;
    public boolean hasDeepParser;
    public boolean hasConditionalParser;
    public Map<String, ParserInfo> subParserInfoMap = new HashMap<String, ParserInfo>();

    public SubParserMap() {
        super(new LinkedHashMap<String, XmlParser>());
    }

    @Override
    protected void childAdded(String key, XmlParser child) {
        clearCache();
    }

    @Override
    protected void childRemoved(String key, XmlParser child) {
        clearCache();
    }

    private void clearCache() {
        cacheBuilded = false;
        hasDeepParser = false;
        hasConditionalParser = false;
        subParserInfoMap.clear();
    }

    public void buildCache() {
        cacheBuilded = true;

        for (Map.Entry<String, XmlParser> entry : target.entrySet()) {
            String path = entry.getKey();
            String[] ps = StringUtils.split(path, DispatchableXmlParser.SUB_PARSER_PATH_SEPERATOR);
            if (ps.length > 1) {
                hasDeepParser = true;
            }
            PathSection[] pathSections = new PathSection[ps.length];
            int i = 0;
            for (String p : ps) {
                PathSection pathSection = PathSection.parse(p);
                if (pathSection.getFixedProperties() != null) {
                    if (i < ps.length - 1) {
                        throw new IllegalArgumentException(
                                "Conditions is only supported in last section [" + path + "].");
                    }
                    hasConditionalParser = true;
                }
                pathSections[i++] = pathSection;
            }

            subParserInfoMap.put(path, new ParserInfo(entry.getValue(), pathSections));
        }
    }

    public ParserInfo getParserInfo(String path) {
        return subParserInfoMap.get(path);
    }
}