1
+ using System ;
2
+ using System . Collections . Generic ;
3
+ using System . Linq ;
4
+ using System . Text ;
5
+ using System . Collections . ObjectModel ;
6
+ using System . Collections ;
7
+
8
+ namespace JSteps . Web . Mvc
9
+ {
10
+ /// <summary>
11
+ /// Provides a read-only collection base for http accept request headers.
12
+ /// </summary>
13
+ /// <remarks>
14
+ /// accept-encoding spec: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
15
+ /// Note: The list is ordered by quality.
16
+ /// </remarks>
17
+ public abstract class AcceptRequestHeaderCollectionBase < T >
18
+ : IList < T >
19
+ where T : AcceptRequestHeaderItem
20
+ {
21
+ private static char _delimiters = ',' ;
22
+ private static char [ ] _parameterDelimiters = { ';' , '=' } ;
23
+
24
+ private readonly List < T > _sortedItems ;
25
+
26
+ protected AcceptRequestHeaderCollectionBase ( string header )
27
+ {
28
+ ArgumentValidation . ValidateArgument ( "header" , header ) ;
29
+
30
+ if ( ! AcceptEmptyHeader )
31
+ {
32
+ throw new ArgumentException ( "Empty accept request header is not allowed." , "header" ) ;
33
+ }
34
+
35
+ _sortedItems = ( from i in Parse ( header ) where ( ! i . IsAsterisk || AcceptAsterisk ) &&
36
+ ( ! i . IsEmpty || AcceptEmptyValue ) orderby i . Quality descending select i ) . ToList ( ) ;
37
+ }
38
+
39
+ #region Helper Methods
40
+
41
+ private List < T > Parse ( string header )
42
+ {
43
+ var items = new List < T > ( ) ;
44
+
45
+ var values = header . Split ( _delimiters ) ;
46
+ foreach ( var value in values )
47
+ {
48
+ var parameters = value . Split ( _parameterDelimiters ) ;
49
+ var keyValuePairs = new Dictionary < string , string > ( ) ;
50
+
51
+ if ( parameters . Count ( ) > 1 )
52
+ {
53
+ var extensions = parameters . Skip ( 1 ) . ToArray ( ) ;
54
+ for ( var i = 0 ; i < extensions . Count ( ) ; i = i + 2 )
55
+ {
56
+ keyValuePairs . Add ( extensions [ i ] . Trim ( ) , extensions [ i + 1 ] . Trim ( ) ) ;
57
+ }
58
+ }
59
+
60
+ var item = CreateItem ( parameters [ 0 ] . Trim ( ) , keyValuePairs ) ;
61
+ if ( ! item . IsEmpty || AcceptEmptyValue )
62
+ {
63
+ items . Add ( item ) ;
64
+ }
65
+ }
66
+
67
+ return items ;
68
+ }
69
+
70
+ private static T CreateItem ( string value , IDictionary < string , string > parameters )
71
+ {
72
+ var constructor = typeof ( T ) . GetConstructor ( new [ ] { value . GetType ( ) , parameters . GetType ( ) } ) ;
73
+ return ( T ) constructor . Invoke ( new object [ ] { value , parameters } ) ;
74
+ }
75
+
76
+ #endregion
77
+
78
+ #region IList<T> Members
79
+
80
+ public T this [ int index ]
81
+ {
82
+ get
83
+ {
84
+ return _sortedItems [ index ] ;
85
+ }
86
+ set
87
+ {
88
+ throw new NotSupportedException ( ) ;
89
+ }
90
+ }
91
+
92
+ public void Add ( T item )
93
+ {
94
+ throw new NotSupportedException ( ) ;
95
+ }
96
+
97
+ public void Clear ( )
98
+ {
99
+ throw new NotSupportedException ( ) ;
100
+ }
101
+
102
+ public bool Contains ( T item )
103
+ {
104
+ return _sortedItems . Contains ( item ) ;
105
+ }
106
+
107
+ public void CopyTo ( T [ ] array , int arrayIndex )
108
+ {
109
+ _sortedItems . CopyTo ( array , arrayIndex ) ;
110
+ }
111
+
112
+ public int Count
113
+ {
114
+ get { return _sortedItems . Count ( ) ; }
115
+ }
116
+
117
+ public IEnumerator < T > GetEnumerator ( )
118
+ {
119
+ return _sortedItems . GetEnumerator ( ) ;
120
+ }
121
+
122
+ IEnumerator IEnumerable . GetEnumerator ( )
123
+ {
124
+ return _sortedItems . GetEnumerator ( ) ;
125
+ }
126
+
127
+ public int IndexOf ( T item )
128
+ {
129
+ return _sortedItems . IndexOf ( item ) ;
130
+ }
131
+
132
+ public void Insert ( int index , T item )
133
+ {
134
+ throw new NotSupportedException ( ) ;
135
+ }
136
+
137
+ public bool IsReadOnly
138
+ {
139
+ get { return true ; }
140
+ }
141
+
142
+ public bool Remove ( T item )
143
+ {
144
+ throw new NotSupportedException ( ) ;
145
+ }
146
+
147
+ public void RemoveAt ( int index )
148
+ {
149
+ throw new NotSupportedException ( ) ;
150
+ }
151
+
152
+ #endregion
153
+
154
+ #region Methods & Properties
155
+
156
+ /// <summary>
157
+ /// Whether or not the asterisk (e.g. "*" or "*/*") encoding is available and allowed
158
+ /// </summary>
159
+ public abstract bool AcceptAsterisk { get ; }
160
+
161
+ /// <summary>
162
+ /// Whether or not empty header is allowed
163
+ /// </summary>
164
+ public abstract bool AcceptEmptyHeader { get ; }
165
+
166
+ /// <summary>
167
+ /// Whether or not empty value is allowed
168
+ /// </summary>
169
+ public abstract bool AcceptEmptyValue { get ; }
170
+
171
+ /// <summary>
172
+ /// Searches for an element that matches the conditions defined by the specified predicate,
173
+ /// and returns the first occurrence within the entire collection.
174
+ /// </summary>
175
+ /// <param name="match">The <see cref="Predicate<T>"/> delegate that defines the conditions
176
+ /// of the element to search for.</param>
177
+ /// <returns>The first element that matches the conditions defined by the specified predicate,
178
+ /// if found; otherwise, the default value for type T.</returns>
179
+ public T Find ( Predicate < T > match )
180
+ {
181
+ return _sortedItems . Find ( match ) ;
182
+ }
183
+
184
+ /// <summary>
185
+ /// Returns the first match found from the given canidates that is accepted
186
+ /// </summary>
187
+ /// <param name="canidates">The list of names to find</param>
188
+ /// <returns>The first <see cref="T"/> match to be found</returns>
189
+ public T FindAccepted ( IEnumerable < string > canidates )
190
+ {
191
+ Predicate < T > predicate = ( T i ) =>
192
+ {
193
+ return ( canidates . Any ( a => a . Equals ( i . Value , StringComparison . OrdinalIgnoreCase ) ) ||
194
+ i . IsAsterisk ) && i . Accepted ;
195
+ } ;
196
+
197
+ return Find ( predicate ) ;
198
+ }
199
+
200
+ #endregion
201
+ }
202
+ }
0 commit comments