/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Collections; using System.Collections.Specialized; using System.Globalization; using System.Reflection; using System.Text; #if !NETCF using System.Web; #endif namespace Apache.NMS.Util { /// /// Class to provide support for Uri query parameters which uses .Net reflection /// to identify and set properties. /// public class URISupport { /// /// Parse a Uri query string of the form ?x=y&z=0 /// into a map of name/value pairs. /// /// The query string to parse. This string should not contain /// Uri escape characters. public static StringDictionary ParseQuery(string query) { StringDictionary map = new StringDictionary(); // strip the initial "?" if(query.StartsWith("?")) { query = query.Substring(1); } // split the query into parameters string[] parameters = query.Split('&'); foreach(string pair in parameters) { if(pair.Length > 0) { string[] nameValue = pair.Split('='); if(nameValue.Length != 2) { throw new NMS.NMSException("Invalid Uri parameter: " + query); } map[nameValue[0]] = nameValue[1]; } } return map; } public static StringDictionary ParseParameters(Uri uri) { return uri.Query == null ? emptyMap() : ParseQuery(stripPrefix(uri.Query, "?")); } /// /// Sets the public properties of a target object using a string map. /// This method uses .Net reflection to identify public properties of /// the target object matching the keys from the passed map. /// /// The object whose properties will be set. /// Map of key/value pairs. /// Key value prefix. This is prepended to the property name /// before searching for a matching key value. public static void SetProperties(object target, StringDictionary map, string prefix) { Type type = target.GetType(); foreach(string key in map.Keys) { if(key.ToLower().StartsWith(prefix.ToLower())) { string bareKey = key.Substring(prefix.Length); PropertyInfo prop = type.GetProperty(bareKey, BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase); if(null == prop) { throw new NMS.NMSException(string.Format("no such property: {0} on class: {1}", bareKey, target.GetType().Name)); } prop.SetValue(target, Convert.ChangeType(map[key], prop.PropertyType, CultureInfo.InvariantCulture), null); } } } public static String UrlDecode(String s) { #if !NETCF return HttpUtility.HtmlDecode(s); #else return Uri.UnescapeDataString(s); #endif } public static String UrlEncode(String s) { #if !NETCF return HttpUtility.HtmlEncode(s); #else return Uri.EscapeUriString(s); #endif } public static String createQueryString(StringDictionary options) { if(options.Count > 0) { StringBuilder rc = new StringBuilder(); bool first = true; foreach(String key in options.Keys) { string value = options[key]; if(first) { first = false; } else { rc.Append("&"); } rc.Append(UrlEncode(key)); rc.Append("="); rc.Append(UrlEncode(value)); } return rc.ToString(); } else { return ""; } } public class CompositeData { private String host; private String scheme; private String path; private Uri[] components; private StringDictionary parameters; private String fragment; public Uri[] Components { get { return components; } set { components = value; } } public String Fragment { get { return fragment; } set { fragment = value; } } public StringDictionary Parameters { get { return parameters; } set { parameters = value; } } public String Scheme { get { return scheme; } set { scheme = value; } } public String Path { get { return path; } set { path = value; } } public String Host { get { return host; } set { host = value; } } public Uri toUri() { StringBuilder sb = new StringBuilder(); if(scheme != null) { sb.Append(scheme); sb.Append(':'); } if(host != null && host.Length != 0) { sb.Append(host); } else { sb.Append('('); for(int i = 0; i < components.Length; i++) { if(i != 0) { sb.Append(','); } sb.Append(components[i].ToString()); } sb.Append(')'); } if(path != null) { sb.Append('/'); sb.Append(path); } if(parameters.Count != 0) { sb.Append("?"); sb.Append(createQueryString(parameters)); } if(fragment != null) { sb.Append("#"); sb.Append(fragment); } return new Uri(sb.ToString()); } } public static String stripPrefix(String value, String prefix) { if(value.StartsWith(prefix)) { return value.Substring(prefix.Length); } return value; } public static CompositeData parseComposite(Uri uri) { CompositeData rc = new CompositeData(); rc.Scheme = uri.Scheme; // URI is one of these formats: // scheme://host:port/path?query // scheme://host/path(URI_1,URI_2,...,URI_N)?query // where URI_x can be any valid URI (including a composite one). // Host and port and path are optional. // This does mean that a URI containing balanced parenthesis are considered // to be composite. This can be a problem if parenthesis are used in another context. // // This routine constructs CompositeData reflecting either of // these forms. Each of the URI_x are stored into the components // of the CompositeData. // // Sample valid URI that should be accepted: // // tcp://192.168.1.1 // tcp://192.168.1.1/ // tcp://192.168.1.1:61616 // tcp://192.168.1.1:61616/ // tcp://machine:61616 // tcp://host:61616/ // failover:(tcp://192.168.1.1:61616?trace=true,tcp://machine:61616?trace=false)?random=true // If this is a composite URI, then strip the scheme // and break up the URI into components. If not, then pass the URI directly. // We detect "compositeness" by the existence of a "(" in the URI containing // balanced parenthesis // Start with original URI #if NET_1_0 || NET_1_1 String ssp = uri.AbsoluteUri.Trim(); #else String ssp = uri.OriginalString.Trim(); #endif // If balanced and existing, assume composite if(checkParenthesis(ssp) && ssp.IndexOf("(") >= 0) { // Composite ssp = stripPrefix(ssp, rc.Scheme).Trim(); ssp = stripPrefix(ssp, ":").Trim(); } else { // Fake a composite URL with parenthesis ssp = "(" + ssp + ")"; } // Handle the composite components parseComposite(uri, rc, ssp); rc.Fragment = uri.Fragment; return rc; } /// /// /// /// /// private static void parseComposite(Uri uri, CompositeData rc, String ssp) { String componentString; String parms; if(!checkParenthesis(ssp)) { throw new ApplicationException(uri.ToString() + ": Not a matching number of '(' and ')' parenthesis"); } int p; int intialParen = ssp.IndexOf("("); if(intialParen >= 0) { rc.Host = ssp.Substring(0, intialParen); p = rc.Host.IndexOf("/"); if(p >= 0) { rc.Path = rc.Host.Substring(p); rc.Host = rc.Host.Substring(0, p); } p = ssp.LastIndexOf(")"); int start = intialParen + 1; int len = p - start; componentString = ssp.Substring(start, len); parms = ssp.Substring(p + 1).Trim(); } else { p = ssp.IndexOf("?"); if(p >= 0) { componentString = ssp.Substring(0, p); parms = ssp.Substring(p); } else { componentString = ssp; parms = ""; } } String[] components = splitComponents(componentString); rc.Components = new Uri[components.Length]; for(int i = 0; i < components.Length; i++) { rc.Components[i] = new Uri(components[i].Trim()); } p = parms.IndexOf("?"); if(p >= 0) { if(p > 0) { rc.Path = stripPrefix(parms.Substring(0, p), "/"); } rc.Parameters = ParseQuery(parms.Substring(p + 1)); } else { if(parms.Length > 0) { rc.Path = stripPrefix(parms, "/"); } rc.Parameters = emptyMap(); } } private static StringDictionary emptyMap() { return new StringDictionary(); } /// /// /// private static String[] splitComponents(String componentString) { ArrayList l = new ArrayList(); int last = 0; int depth = 0; char[] chars = componentString.ToCharArray(); for(int i = 0; i < chars.Length; i++) { switch(chars[i]) { case '(': depth++; break; case ')': depth--; break; case ',': if(depth == 0) { String s = componentString.Substring(last, i - last); l.Add(s); last = i + 1; } break; default: break; } } String ending = componentString.Substring(last); if(ending.Length != 0) { l.Add(ending); } String[] rc = new String[l.Count]; l.CopyTo(rc); return rc; } public static bool checkParenthesis(String str) { bool result = true; if(str != null) { int open = 0; int closed = 0; int i = 0; while((i = str.IndexOf('(', i)) >= 0) { i++; open++; } i = 0; while((i = str.IndexOf(')', i)) >= 0) { i++; closed++; } result = (open == closed); } return result; } } }