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