Java tutorial
/* * 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); } }