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.text; import java.util.HashMap; import java.util.Map; import org.apache.commons.lang.StringUtils; import com.bstek.dorado.util.Assert; /** * ?<br> * ?? * * <pre> * ? ??1:1; ??2:2; ??3:3 * </pre> * @author Benny Bao (mailto:benny.bao@bstek.com) * @since Feb 28, 2008 */ public abstract class DispatchableTextParser implements TextParser { /** * ????? */ public static final String HEADER_ATTRIBUTE = "$header"; private static final int BEFORE_HEADER = 0; private static final int IN_HEADER = 1; private static final int AFTER_HEADER = 2; private static final int BEFORE_ATTRIBUTE_NAME = 3; private static final int IN_ATTRIBUTE_NAME = 4; private static final int AFTER_ATTRIBUTE_NAME = 5; private static final int BEFORE_ATTRIBUTE_VALUE = 6; /** * ???<br> * ????????WILDCARD? */ public static final String WILDCARD = "*"; private Map<String, TextParser> attributeParsers = new HashMap<String, TextParser>(); private Map<String, TextParser> subParsers = new HashMap<String, TextParser>(); /** * ?????<br> * ?????$header */ public boolean supportsHeader() { return false; } /** * ???? * @param constraint ?????null{@link #WILDCARD} * ?? * @param parser ? */ public void registerAttributeParser(String constraint, TextParser parser) { Assert.notNull(parser, "[parser] is required"); constraint = StringUtils.defaultString(constraint, WILDCARD); attributeParsers.put(constraint, parser); } /** * ?Map??? * @return ?Map? */ public Map<String, TextParser> getAttributeParsers() { return attributeParsers; } /** * ????? * @param constraint ?? * ??????WILDCARD?? */ protected TextParser findAttributeParser(String constraint) { constraint = StringUtils.defaultString(constraint, WILDCARD); TextParser parser = attributeParsers.get(constraint); if (parser == null && !WILDCARD.equals(constraint)) { parser = attributeParsers.get(WILDCARD); } return parser; } /** * ????? * @param constraint ????? ?nullWILDCARD?? * @param parser ?? */ public void registerSubParser(String constraint, TextParser parser) { Assert.notNull(parser, "[parser] is required"); constraint = StringUtils.defaultString(constraint, WILDCARD); subParsers.put(constraint, parser); } /** * ?? Map???? * @return ??Map? */ public Map<String, TextParser> getSubParsers() { return subParsers; } /** * ?????? * @param constraint ?? * ??????WILDCARD??\\ * @param context * @return ??? * @throws Exception */ protected TextParser findSubParser(String constraint, TextParseContext context) throws Exception { constraint = StringUtils.defaultString(constraint, WILDCARD); TextParser parser = subParsers.get(constraint); if (parser == null && !WILDCARD.equals(constraint)) { parser = subParsers.get(WILDCARD); } return parser; } /** * ???? */ protected boolean isValidPropertyNameChar(char c) { return (c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || c == '-' || c == '_' || c == '$'); } /** * ??<br> * ?? */ protected boolean isIgnoredChar(char c) { return (c == ' ' || c == '\n' || c == '\r' || c == '\t'); } /** * ????Map? */ public Object parse(char[] charArray, TextParseContext context) throws Exception { return parseToAttributes(charArray, context); } /** * ????Map?Map??? ???? * {@link #HEADER_ATTRIBUTE}?? */ protected Map<String, Object> parseToAttributes(char[] charArray, TextParseContext context) throws Exception { Map<String, Object> attributes = new HashMap<String, Object>(); int status; if (supportsHeader()) { String header = parseHeader(charArray, context); if (header != null) attributes.put(HEADER_ATTRIBUTE, header); } status = BEFORE_ATTRIBUTE_NAME; StringBuffer nameStack = new StringBuffer(); for (int currentIndex = context.getCurrentIndex(); currentIndex < charArray.length; currentIndex++) { char c = charArray[currentIndex]; context.setCurrentIndex(currentIndex); switch (status) { case BEFORE_ATTRIBUTE_NAME: if (isValidPropertyNameChar(c)) { status = IN_ATTRIBUTE_NAME; nameStack.append(c); } else if (isIgnoredChar(c)) { continue; } else if (c == ':') { throw new TextParseException(charArray, context); } break; case IN_ATTRIBUTE_NAME: if (isValidPropertyNameChar(c)) { nameStack.append(c); } else if (isIgnoredChar(c)) { status = AFTER_ATTRIBUTE_NAME; continue; } else if (c == ':') { status = BEFORE_ATTRIBUTE_VALUE; continue; } else if (c == ';') { throw new TextParseException(charArray, context); } break; case AFTER_ATTRIBUTE_NAME: if (isValidPropertyNameChar(c)) { throw new TextParseException(charArray, context); } else if (isIgnoredChar(c)) { continue; } else if (c == ':') { status = BEFORE_ATTRIBUTE_VALUE; continue; } else if (c == ';') { throw new TextParseException(charArray, context); } break; case BEFORE_ATTRIBUTE_VALUE: if (nameStack.length() == 0) { throw new TextParseException("Attribute name can not be empty.", charArray, context); } else { String name = nameStack.toString(); char firstC = name.charAt(0); if (firstC >= '0' && firstC <= '9') { throw new TextParseException("Attribute name [" + name + "] can not start with number."); } Object value = dispatchAttribute(name, charArray, context); attributes.put(name, value); currentIndex = context.getCurrentIndex(); status = BEFORE_ATTRIBUTE_NAME; nameStack.setLength(0); } break; } } if (nameStack.length() > 0) { throw new TextParseException(charArray, context); } return attributes; } protected String parseHeader(char[] charArray, TextParseContext context) throws TextParseException { int status; status = BEFORE_HEADER; StringBuffer headerStack = new StringBuffer(); int startIndex = context.getCurrentIndex(); for (int currentIndex = startIndex; currentIndex < charArray.length && status != AFTER_HEADER; currentIndex++) { char c = charArray[currentIndex]; context.setCurrentIndex(currentIndex); switch (status) { case BEFORE_HEADER: if (isIgnoredChar(c)) { continue; } else if (c == ':' || c == ';') { throw new TextParseException(charArray, context); } else { headerStack.append(c); status = IN_HEADER; } break; case IN_HEADER: if (isIgnoredChar(c)) { status = AFTER_HEADER; } else if (c == ':' || c == ';') { headerStack.setLength(0); status = AFTER_HEADER; } else { headerStack.append(c); } break; } } if (status != AFTER_HEADER) { context.increaseCurrentIndex(); } String header = null; if (headerStack.length() > 0) { header = headerStack.toString(); } else { context.setCurrentIndex(startIndex); } return header; } /** * ????????? * @param constraint ?? * @param charArray ?? * @param context ? * @return ? * @throws Exception */ protected Object dispatchAttribute(String constraint, char[] charArray, TextParseContext context) throws Exception { TextParser parser = findAttributeParser(constraint); if (parser != null) { return parser.parse(charArray, context); } else { return null; } } }