/** * Project: dubbo.registry.server * * File Created at Oct 19, 2010 * $Id: ParseUtils.java 181192 2012-06-21 05:05:47Z tony.chenl $ * * Copyright 1999-2100 Alibaba.com Corporation Limited. * All rights reserved. * * This software is the confidential and proprietary information of * Alibaba Company. ("Confidential Information"). You shall not * disclose such Confidential Information and shall use it only in * accordance with the terms of the license agreement you entered into * with Alibaba.com. */ package com.alibaba.dubbo.registry.common.route; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.alibaba.dubbo.common.utils.StringUtils; /** * 字符串解析相关的工具方法,涉及interpolation、Glob模式、Query字串、Service URL处理。 * * @author william.liangf * @author ding.lid */ public class ParseUtils { public static String METHOD_SPLIT = ","; private ParseUtils() {} private static Pattern VARIABLE_PATTERN = Pattern.compile( "\\$\\s*\\{?\\s*([\\._0-9a-zA-Z]+)\\s*\\}?"); /** * 执行interpolation(变量插入)。 * * @param expression 含有变量的表达式字符串。表达式中的变量名也可以用<code>{}</code>括起来。 * @param params 变量集。变量名可以包含<code>.</code>、<code>_</code>字符。 * @return 完成interpolation后的字符串。 如:<code><pre>xxx${name}zzz -> xxxjerryzzz</pre></code>(其中变量name="jerry") * @throws IllegalStateException 表达式字符串中使用到的变量 在变量集中没有 */ // FIMXE 抛出IllegalStateException异常,是否合适?! public static String interpolate(String expression, Map<String, String> params) { if (expression == null || expression.length() == 0) { throw new IllegalArgumentException("glob pattern is empty!"); } if (expression.indexOf('$') < 0) { return expression; } Matcher matcher = VARIABLE_PATTERN.matcher(expression); StringBuffer sb = new StringBuffer(); while (matcher.find()) { // 逐个匹配 String key = matcher.group(1); String value = params == null ? null: params.get(key); if (value == null) { value = ""; } matcher.appendReplacement(sb, value); } matcher.appendTail(sb); return sb.toString(); } public static List<String> interpolate(List<String> expressions, Map<String, String> params) { List<String> ret = new ArrayList<String>(); if(null == expressions || expressions.isEmpty()) { return ret; } for(String expr : expressions) { ret.add(interpolate(expr, params)); } return ret; } /** * 匹配Glob模式。目前的实现只支持<code>*</code>,且只支持一个。不支持<code>?</code>。 * @return 对于方法参数pattern或是value为<code>null</code>的情况,直接返回<code>false</code>。 */ public static boolean isMatchGlobPattern(String pattern, String value) { if ("*".equals(pattern)) return true; if((pattern == null || pattern.length() == 0) && (value == null || value.length() == 0)) return true; if((pattern == null || pattern.length() == 0) || (value == null || value.length() == 0)) return false; int i = pattern.lastIndexOf('*'); // 没有找到星号 if(i == -1) { return value.equals(pattern); } // 星号在末尾 else if (i == pattern.length() - 1) { return value.startsWith(pattern.substring(0, i)); } // 星号的开头 else if (i == 0) { return value.endsWith(pattern.substring(i + 1)); } // 星号的字符串的中间 else { String prefix = pattern.substring(0, i); String suffix = pattern.substring(i + 1); return value.startsWith(prefix) && value.endsWith(suffix); } } /** * 是否匹配Glob模式。Glob模式是要插值的表达式。Glob模式有多个,只要匹配一个模式,就认为匹配成功。 * * @param patternsNeedInterpolate 多个要进行插值的Glob模式 * @param interpolateParams 用于插值的变量集 * @param value 进行Glob模式的值 */ public static boolean isMatchGlobPatternsNeedInterpolate( Collection<String> patternsNeedInterpolate, Map<String, String> interpolateParams, String value) { if(patternsNeedInterpolate != null && ! patternsNeedInterpolate.isEmpty()) { for (String patternNeedItp : patternsNeedInterpolate) { if(StringUtils.isEmpty(patternNeedItp)) { continue; } // FIXME ERROR!! 原来的实现,这里只和第一个不为空的pattern比较,返回对应的结果! 和梁飞确认 String pattern = interpolate(patternNeedItp, interpolateParams); if(isMatchGlobPattern(pattern, value)) { return true; } } } return false; } /** * 返回集合中与Glob模式匹配的条目。 */ public static Set<String> filterByGlobPattern(String pattern, Collection<String> values) { Set<String> ret = new HashSet<String>(); if(pattern == null || values == null) { return ret; } for(String v : values) { if(isMatchGlobPattern(pattern, v)) { ret.add(v); } } return ret; } /** * 找到了配合Glob模式的字符串。模式有多个,只要匹配一个模式,就返回这个字符串。 */ public static Set<String> filterByGlobPattern(Collection<String> patterns, Collection<String> values) { Set<String> ret = new HashSet<String>(); if(null == patterns || values == null || patterns.isEmpty() || values.isEmpty()) { return ret; } for(String p : patterns) { for(String v : values) { if(isMatchGlobPattern(p, v)) { ret.add(v); } } } return ret; } /** * 两个Glob模式是否有交集。 */ public static boolean hasIntersection(String glob1, String glob2) { if (null == glob1 || null == glob2) { return false; } if (glob1.contains("*") && glob2.contains("*")) { int index1 = glob1.indexOf("*"); int index2 = glob2.indexOf("*"); String s11 = glob1.substring(0, index1); String s12 = glob1.substring(index1 + 1, glob1.length()); String s21 = glob2.substring(0, index2); String s22 = glob2.substring(index2 + 1, glob2.length()); if(!s11.startsWith(s21) && !s21.startsWith(s11)) return false; if(!s12.endsWith(s22) && !s22.endsWith(s12)) return false; return true; } else if (glob1.contains("*")) { return isMatchGlobPattern(glob1, glob2); } else if (glob2.contains("*")) { return isMatchGlobPattern(glob2, glob1); } else { return glob1.equals(glob2); } } private static Pattern QUERY_PATTERN = Pattern .compile("([&=]?)\\s*([^&=\\s]+)"); /** * 把Query String解析成Map。对于有只有Key的串<code>key3=</code>,忽略。 * * @param keyPrefix 在输出的Map的Key加上统一前缀。 * @param query Query String,形如:<code>key1=value1&key2=value2</code> * @return Query String为<code>key1=value1&key2=value2</code>,前缀为<code>pre.</code>时, * 则返回<code>Map{pre.key1=value1, pre.key=value2}</code>。 */ // FIXME 抛出的是IllegalStateException异常,是否合理?! public static Map<String, String> parseQuery(String keyPrefix, String query) { if (query == null) return new HashMap<String, String>(); if (keyPrefix == null) keyPrefix = ""; Matcher matcher = QUERY_PATTERN.matcher(query); Map<String, String> routeQuery = new HashMap<String, String>(); String key = null; while (matcher.find()) { // 逐个匹配 String separator = matcher.group(1); String content = matcher.group(2); if (separator == null || separator.length() == 0 || "&".equals(separator)) { if (key != null) throw new IllegalStateException("Illegal query string \"" + query + "\", The error char '" + separator + "' at index " + matcher.start() + " before \"" + content + "\"."); key = content; } else if ("=".equals(separator)) { if (key == null) throw new IllegalStateException("Illegal query string \"" + query + "\", The error char '" + separator + "' at index " + matcher.start() + " before \"" + content + "\"."); routeQuery.put(keyPrefix + key, content); key = null; } else { if (key == null) throw new IllegalStateException("Illegal query string \"" + query + "\", The error char '" + separator + "' at index " + matcher.start() + " before \"" + content + "\"."); } } /*if (key != null) throw new IllegalStateException("Illegal route rule \"" + query + "\", The error in the end char: " + key);*/ return routeQuery; } public static Map<String, String> parseQuery(String query) { return parseQuery("", query); } private static final ConcurrentMap<String, Pattern> REPLACE_PARAMETER_PATTERNS = new ConcurrentHashMap<String, Pattern>(); /** * 替换url中参数的值。 */ public static String replaceParameter(String query, String key, String value) { if (query == null || query.length() == 0) { return key + "=" + value; } if (query.indexOf(key + "=") == -1) { return query + "&" + key + "=" + value; } Pattern pattern = REPLACE_PARAMETER_PATTERNS.get(key); if (pattern == null) { pattern = Pattern.compile(key.replaceAll("([^(_0-9A-Za-z)])", "\\\\$0") + "=[^&]+"); } Matcher matcher = pattern.matcher(query); StringBuffer sb = new StringBuffer(); while (matcher.find()) { matcher.appendReplacement(sb, (key + "=" + value).replace("$", "\\$")); } matcher.appendTail(sb); return sb.toString(); } public static String appendParamToUri(String uri, String name, String value) { if (StringUtils.isEmpty(name) || StringUtils.isEmpty(value)) return uri; if (uri.indexOf('?') != -1) { uri += "&" + name + "=" + value; } else { uri += "?" + name + "=" + value; } return uri; } public static String appendParamsToUri(String uri, Map<String, String> params) { StringBuilder buf = new StringBuilder(uri); boolean first = (uri.indexOf('?') < 0); for(Map.Entry<String, String> entry : params.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); if(StringUtils.isEmpty(key) || StringUtils.isEmpty(value)) continue; if (first) { buf.append("?"); first = false; } else { buf.append("&"); } buf.append(key); buf.append("="); buf.append(value); } return buf.toString(); } public static boolean matchEndStarPattern(String value, String pattern) { if(!pattern.endsWith("*")) throw new IllegalArgumentException("not end star pattern!"); String perfix = pattern.substring(0, pattern.length() - 1); return value.startsWith(perfix); } }