blob: 5a3d686a4a554db800d8d98b29beb392c731c09d [file] [log] [blame]
/*
* 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.Generic;
using System.Collections.Specialized;
using System.Globalization;
using System.Reflection;
using System.Text;
#if !NETCF
using System.Web;
#endif
namespace Apache.NMS.Util
{
/// <summary>
/// Class to provide support for Uri query parameters which uses .Net reflection
/// to identify and set properties.
/// </summary>
public class URISupport
{
/// <summary>
/// Given a string that could be a Composite Uri that uses syntax not compatible
/// with the .NET Uri class such as an ActiveMQ failover Uri formatted as
/// "failover://(tcp://localhost:61616)", the initial '://' must be changed
/// to ':(' so that the Uri class doesn't attempt to parse the '(tcp:' as
/// the Uri's Authority as that is not a valid host name.
/// </summary>
/// <param name="uriString">
/// A string that could be a Composite Uri that uses syntax not compatible
/// with the .NET Uri class
/// </param>
public static Uri CreateCompatibleUri(string uriString)
{
string sanitized = uriString.Replace("://(", ":(");
return new Uri(sanitized);
}
/// <summary>
/// Parse a Uri query string of the form ?x=y&amp;z=0
/// into a map of name/value pairs.
/// </summary>
/// <param name="query">The query string to parse. This string should not contain
/// Uri escape characters.</param>
public static StringDictionary ParseQuery(String query)
{
StringDictionary map = new StringDictionary();
if(String.IsNullOrEmpty(query))
{
return EmptyMap;
}
// 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 NMSException(string.Format("Invalid Uri parameter: {0}", query));
}
map[UrlDecode(nameValue[0])] = UrlDecode(nameValue[1]);
}
}
return map;
}
public static StringDictionary ParseParameters(Uri uri)
{
return (uri.Query == null
? EmptyMap
: ParseQuery(StripPrefix(uri.Query, "?")));
}
/// <summary>
/// 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.
/// </summary>
/// <param name="target">The object whose properties will be set.</param>
/// <param name="map">Map of key/value pairs.</param>
public static void SetProperties(object target, StringDictionary map)
{
Type type = target.GetType();
foreach(string key in map.Keys)
{
PropertyInfo prop = type.GetProperty(key,
BindingFlags.FlattenHierarchy
| BindingFlags.Public
| BindingFlags.Instance
| BindingFlags.IgnoreCase);
if(null != prop)
{
prop.SetValue(target, Convert.ChangeType(map[key], prop.PropertyType, CultureInfo.InvariantCulture), null);
}
else
{
FieldInfo field = type.GetField(key,
BindingFlags.FlattenHierarchy
| BindingFlags.Public
| BindingFlags.Instance
| BindingFlags.IgnoreCase);
if(null != field)
{
field.SetValue(target, Convert.ChangeType(map[key], field.FieldType, CultureInfo.InvariantCulture));
}
else
{
throw new NMSException(string.Format("No such property or field: {0} on class: {1}", key, target.GetType().Name));
}
}
}
}
/// <summary>
/// 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.
/// </summary>
/// <param name="target">The object whose properties will be set.</param>
/// <param name="map">Map of key/value pairs.</param>
/// <param name="prefix">Key value prefix. This is prepended to the property name
/// before searching for a matching key value.</param>
public static void SetProperties(object target, StringDictionary map, string prefix)
{
Type type = target.GetType();
List<String> matches = new List<String>();
foreach(string key in map.Keys)
{
if(key.StartsWith(prefix, StringComparison.InvariantCultureIgnoreCase))
{
string bareKey = key.Substring(prefix.Length);
PropertyInfo prop = type.GetProperty(bareKey,
BindingFlags.FlattenHierarchy
| BindingFlags.Public
| BindingFlags.Instance
| BindingFlags.IgnoreCase);
if(null != prop)
{
prop.SetValue(target, Convert.ChangeType(map[key], prop.PropertyType, CultureInfo.InvariantCulture), null);
}
else
{
FieldInfo field = type.GetField(bareKey,
BindingFlags.FlattenHierarchy
| BindingFlags.Public
| BindingFlags.Instance
| BindingFlags.IgnoreCase);
if(null != field)
{
field.SetValue(target, Convert.ChangeType(map[key], field.FieldType, CultureInfo.InvariantCulture));
}
else
{
throw new NMSException(string.Format("No such property or field: {0} on class: {1}", bareKey, target.GetType().Name));
}
}
// store for later removal.
matches.Add(key);
}
}
// Remove all the properties we set so they are used again later.
foreach(string match in matches)
{
map.Remove(match);
}
}
public static StringDictionary GetProperties(StringDictionary props, string prefix)
{
if(props == null)
{
throw new Exception("Properties Object was null");
}
StringDictionary result = new StringDictionary();
foreach(string key in props.Keys)
{
if(key.StartsWith(prefix, StringComparison.InvariantCultureIgnoreCase))
{
string bareKey = key.Substring(prefix.Length);
String value = props[key];
result[bareKey] = value;
}
}
return result;
}
public static StringDictionary ExtractProperties(StringDictionary props, string prefix)
{
if(props == null)
{
throw new Exception("Properties Object was null");
}
StringDictionary result = new StringDictionary();
List<String> matches = new List<String>();
foreach(string key in props.Keys)
{
if(key.StartsWith(prefix, StringComparison.InvariantCultureIgnoreCase))
{
String value = props[key];
result[key] = value;
matches.Add(key);
}
}
foreach(string match in matches)
{
props.Remove(match);
}
return result;
}
public static String UrlDecode(String s)
{
#if !NETCF
return HttpUtility.UrlDecode(s);
#else
return Uri.UnescapeDataString(s);
#endif
}
public static String UrlEncode(String s)
{
#if !NETCF
return HttpUtility.UrlEncode(s);
#else
return Uri.EscapeUriString(s);
#endif
}
public static String CreateQueryString(StringDictionary options)
{
if(options != null && 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 static Uri CreateRemainingUri(Uri originalUri, StringDictionary parameters)
{
string s = CreateQueryString(parameters);
if(String.IsNullOrEmpty(s))
{
s = null;
}
return CreateUriWithQuery(originalUri, s);
}
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(!string.IsNullOrEmpty(host))
{
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, StringComparison.InvariantCultureIgnoreCase))
{
return value.Substring(prefix.Length);
}
return value;
}
public static Uri CreateUriWithQuery(Uri uri, string query)
{
if(!String.IsNullOrEmpty(query) && !query.StartsWith("?"))
{
query = "?" + query;
}
if(String.IsNullOrEmpty(uri.Query))
{
return new Uri(uri.OriginalString + query);
}
else
{
string originalUri = uri.OriginalString;
int queryDelimPos = originalUri.LastIndexOf('?');
int compositeDelimPos = originalUri.LastIndexOf(')');
if(queryDelimPos <= compositeDelimPos)
{
// No Query or the Query is part of an inner Composite.
return new Uri(originalUri + query);
}
else
{
// Outer Uri has a Query or not a Composite Uri with a Query
string strippedUri = originalUri.Substring(0, queryDelimPos);
return new Uri(strippedUri + query);
}
}
}
public static Uri RemoveQuery(Uri original)
{
return CreateUriWithQuery(original, null);
}
public static CompositeData ParseComposite(Uri uri)
{
CompositeData rc = new CompositeData();
rc.Scheme = uri.Scheme;
// Start with original URI
//String ssp = uri.Authority + uri.PathAndQuery;
String ssp = uri.OriginalString;
ssp = StripPrefix(ssp, rc.Scheme + ":");
ssp = StripPrefix(ssp, "//");
int lastPoundPos = ssp.LastIndexOf("#");
int lastParendPos = ssp.LastIndexOf(")");
// Only include a Fragment that's outside any Composte sections.
if(lastPoundPos > lastParendPos)
{
rc.Fragment = ssp.Substring(lastPoundPos);
ssp = ssp.Substring(0, lastPoundPos);
}
// Ensure any embedded URIs don't have malformed authority's by changing
// them from '://(' which would cause the .NET Uri class to attempt to validate
// the authority as a hostname with, ':(' which is valid.
ssp = ssp.Replace("://(", ":(");
// Handle the composite components
ParseComposite(uri, rc, ssp);
return rc;
}
/// <summary>
/// </summary>
/// <param name="uri"></param>
/// <param name="rc"></param>
/// <param name="ssp"></param>
private static void ParseComposite(Uri uri, CompositeData rc, String ssp)
{
String componentString;
String parms;
if(!CheckParenthesis(ssp))
{
throw new NMSException(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
{
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
{
get { return new StringDictionary(); }
}
/// <summary>
/// </summary>
/// <param name="componentString"></param>
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;
}
}
}