1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
|
//-----------------------------------------------------------------------
// <copyright file="Util.cs" company="Outercurve Foundation">
// Copyright (c) Outercurve Foundation. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace DotNetOpenAuth {
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.Http.Headers;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.UI;
using DotNetOpenAuth.Configuration;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.Messaging.Reflection;
using Validation;
/// <summary>
/// A grab-bag utility class.
/// </summary>
internal static class Util {
/// <summary>
/// The base namespace for this library from which all other namespaces derive.
/// </summary>
internal const string DefaultNamespace = "DotNetOpenAuth";
/// <summary>
/// A lazily-assembled string that describes the version of the library.
/// </summary>
private static readonly Lazy<string> libraryVersionLazy = new Lazy<string>(delegate {
var assembly = Assembly.GetExecutingAssembly();
string assemblyFullName = assembly.FullName;
bool official = assemblyFullName.Contains("PublicKeyToken=d0acff3d13b42a9d");
assemblyFullName = assemblyFullName.Replace(assembly.GetName().Version.ToString(), AssemblyFileVersion);
// We use InvariantCulture since this is used for logging.
return string.Format(CultureInfo.InvariantCulture, "{0} ({1})", assemblyFullName, official ? "official" : "private");
});
/// <summary>
/// A lazily-assembled string that describes the version of the library.
/// </summary>
private static readonly Lazy<ProductInfoHeaderValue> libraryVersionHeaderLazy = new Lazy<ProductInfoHeaderValue>(delegate {
var assemblyName = Assembly.GetExecutingAssembly().GetName();
return new ProductInfoHeaderValue(assemblyName.Name, AssemblyFileVersion);
});
/// <summary>
/// The web.config file-specified provider of web resource URLs.
/// </summary>
private static IEmbeddedResourceRetrieval embeddedResourceRetrieval = MessagingElement.Configuration.EmbeddedResourceRetrievalProvider.CreateInstance(null, false, null);
/// <summary>
/// Gets a human-readable description of the library name and version, including
/// whether the build is an official or private one.
/// </summary>
internal static string LibraryVersion {
get { return libraryVersionLazy.Value; }
}
/// <summary>
/// Gets an HTTP header that can be included in outbound requests.
/// </summary>
internal static ProductInfoHeaderValue LibraryVersionHeader {
get { return libraryVersionHeaderLazy.Value; }
}
/// <summary>
/// Gets the assembly file version of the executing assembly, otherwise falls back to the assembly version.
/// </summary>
internal static string AssemblyFileVersion {
get {
var assembly = Assembly.GetExecutingAssembly();
var attributes = assembly.GetCustomAttributes(typeof(AssemblyFileVersionAttribute), false);
if (attributes.Length == 1) {
var fileVersionAttribute = (AssemblyFileVersionAttribute)attributes[0];
return fileVersionAttribute.Version;
}
return assembly.GetName().Version.ToString();
}
}
/// <summary>
/// Tests for equality between two objects. Safely handles the case where one or both are null.
/// </summary>
/// <typeparam name="T">The type of objects been checked for equality.</typeparam>
/// <param name="first">The first object.</param>
/// <param name="second">The second object.</param>
/// <returns><c>true</c> if the two objects are equal; <c>false</c> otherwise.</returns>
internal static bool EqualsNullSafe<T>(this T first, T second) where T : class {
// If one is null and the other is not...
if (object.ReferenceEquals(first, null) ^ object.ReferenceEquals(second, null)) {
return false;
}
// If both are null... (we only check one because we already know both are either null or non-null)
if (object.ReferenceEquals(first, null)) {
return true;
}
// Neither are null. Delegate to the Equals method.
return first.Equals(second);
}
/// <summary>
/// Prepares a dictionary for printing as a string.
/// </summary>
/// <typeparam name="K">The type of the key.</typeparam>
/// <typeparam name="V">The type of the value.</typeparam>
/// <param name="pairs">The dictionary or sequence of name-value pairs.</param>
/// <returns>An object whose ToString method will perform the actual work of generating the string.</returns>
/// <remarks>
/// The work isn't done until (and if) the
/// <see cref="Object.ToString"/> method is actually called, which makes it great
/// for logging complex objects without being in a conditional block.
/// </remarks>
internal static object ToStringDeferred<K, V>(this IEnumerable<KeyValuePair<K, V>> pairs) {
return new DelayedToString<IEnumerable<KeyValuePair<K, V>>>(
pairs,
p => {
Requires.NotNull(pairs, "pairs");
var dictionary = pairs as IDictionary<K, V>;
var messageDictionary = pairs as MessageDictionary;
StringBuilder sb = new StringBuilder(dictionary != null ? dictionary.Count * 40 : 200);
foreach (var pair in pairs) {
var key = pair.Key.ToString();
string value = pair.Value.ToString();
if (messageDictionary != null && messageDictionary.Description.Mapping.ContainsKey(key) && messageDictionary.Description.Mapping[key].IsSecuritySensitive) {
value = "********";
}
sb.AppendFormat("\t{0}: {1}{2}", key, value, Environment.NewLine);
}
return sb.ToString();
});
}
/// <summary>
/// Offers deferred ToString processing for a list of elements, that are assumed
/// to generate just a single-line string.
/// </summary>
/// <typeparam name="T">The type of elements contained in the list.</typeparam>
/// <param name="list">The list of elements.</param>
/// <returns>An object whose ToString method will perform the actual work of generating the string.</returns>
internal static object ToStringDeferred<T>(this IEnumerable<T> list) {
return ToStringDeferred<T>(list, false);
}
/// <summary>
/// Offers deferred ToString processing for a list of elements.
/// </summary>
/// <typeparam name="T">The type of elements contained in the list.</typeparam>
/// <param name="list">The list of elements.</param>
/// <param name="multiLineElements">if set to <c>true</c>, special formatting will be applied to the output to make it clear where one element ends and the next begins.</param>
/// <returns>An object whose ToString method will perform the actual work of generating the string.</returns>
internal static object ToStringDeferred<T>(this IEnumerable<T> list, bool multiLineElements) {
return new DelayedToString<IEnumerable<T>>(
list,
l => {
// Code contracts not allowed in generator methods.
ErrorUtilities.VerifyArgumentNotNull(l, "l");
string newLine = Environment.NewLine;
////Assumes.True(newLine != null && newLine.Length > 0);
StringBuilder sb = new StringBuilder();
if (multiLineElements) {
sb.AppendLine("[{");
foreach (T obj in l) {
// Prepare the string repersentation of the object
string objString = obj != null ? obj.ToString() : "<NULL>";
// Indent every line printed
objString = objString.Replace(newLine, Environment.NewLine + "\t");
sb.Append("\t");
sb.Append(objString);
if (!objString.EndsWith(Environment.NewLine, StringComparison.Ordinal)) {
sb.AppendLine();
}
sb.AppendLine("}, {");
}
if (sb.Length > 2 + Environment.NewLine.Length) { // if anything was in the enumeration
sb.Length -= 2 + Environment.NewLine.Length; // trim off the last ", {\r\n"
} else {
sb.Length -= 1 + Environment.NewLine.Length; // trim off the opening {
}
sb.Append("]");
return sb.ToString();
} else {
sb.Append("{");
foreach (T obj in l) {
sb.Append(obj != null ? obj.ToString() : "<NULL>");
sb.AppendLine(",");
}
if (sb.Length > 1) {
sb.Length -= 1;
}
sb.Append("}");
return sb.ToString();
}
});
}
/// <summary>
/// Gets the web resource URL from a Page or <see cref="IEmbeddedResourceRetrieval"/> object.
/// </summary>
/// <param name="someTypeInResourceAssembly">Some type in resource assembly.</param>
/// <param name="manifestResourceName">Name of the manifest resource.</param>
/// <returns>An absolute URL</returns>
internal static string GetWebResourceUrl(Type someTypeInResourceAssembly, string manifestResourceName) {
Page page;
IEmbeddedResourceRetrieval retrieval;
if (embeddedResourceRetrieval != null) {
Uri url = embeddedResourceRetrieval.GetWebResourceUrl(someTypeInResourceAssembly, manifestResourceName);
return url != null ? url.AbsoluteUri : null;
} else if ((page = HttpContext.Current.CurrentHandler as Page) != null) {
return page.ClientScript.GetWebResourceUrl(someTypeInResourceAssembly, manifestResourceName);
} else if ((retrieval = HttpContext.Current.CurrentHandler as IEmbeddedResourceRetrieval) != null) {
return retrieval.GetWebResourceUrl(someTypeInResourceAssembly, manifestResourceName).AbsoluteUri;
} else {
throw new InvalidOperationException(
string.Format(
CultureInfo.CurrentCulture,
Strings.EmbeddedResourceUrlProviderRequired,
string.Join(", ", new string[] { typeof(Page).FullName, typeof(IEmbeddedResourceRetrieval).FullName })));
}
}
/// <summary>
/// Creates a dictionary of a sequence of elements and the result of an asynchronous transform,
/// allowing the async work to proceed concurrently.
/// </summary>
/// <typeparam name="TSource">The type of the source.</typeparam>
/// <typeparam name="TResult">The type of the result.</typeparam>
/// <param name="source">The source.</param>
/// <param name="transform">The transform.</param>
/// <returns>A dictionary populated with the results of the transforms.</returns>
internal static async Task<Dictionary<TSource, TResult>> ToDictionaryAsync<TSource, TResult>(
this IEnumerable<TSource> source, Func<TSource, Task<TResult>> transform) {
var taskResults = source.ToDictionary(s => s, transform);
await Task.WhenAll(taskResults.Values);
return taskResults.ToDictionary(p => p.Key, p => p.Value.Result);
}
/// <summary>
/// Manages an individual deferred ToString call.
/// </summary>
/// <typeparam name="T">The type of object to be serialized as a string.</typeparam>
private class DelayedToString<T> {
/// <summary>
/// The object that will be serialized if called upon.
/// </summary>
private readonly T obj;
/// <summary>
/// The method used to serialize <see cref="obj"/> to string form.
/// </summary>
private readonly Func<T, string> toString;
/// <summary>
/// Initializes a new instance of the DelayedToString class.
/// </summary>
/// <param name="obj">The object that may be serialized to string form.</param>
/// <param name="toString">The method that will serialize the object if called upon.</param>
public DelayedToString(T obj, Func<T, string> toString) {
Requires.NotNull(toString, "toString");
this.obj = obj;
this.toString = toString;
}
/// <summary>
/// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
/// </summary>
/// <returns>
/// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
/// </returns>
public override string ToString() {
return this.toString(this.obj) ?? string.Empty;
}
}
}
}
|