Merge pull request #587 from madewokherd/gdipdllmap
[mono.git] / mcs / class / System.Net.Http / System.Net.Http.Headers / RangeHeaderValue.cs
1 //
2 // RangeHeaderValue.cs
3 //
4 // Authors:
5 //      Marek Safar  <marek.safar@gmail.com>
6 //
7 // Copyright (C) 2011 Xamarin Inc (http://www.xamarin.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 using System.Collections.Generic;
30 using System.Text;
31
32 namespace System.Net.Http.Headers
33 {
34         public class RangeHeaderValue : ICloneable
35         {
36                 List<RangeItemHeaderValue> ranges;
37                 string unit;
38
39                 public RangeHeaderValue ()
40                 {
41                         unit = "bytes";
42                 }
43
44                 public RangeHeaderValue (long? from, long? to)
45                         : this ()
46                 {
47                         Ranges.Add (new RangeItemHeaderValue (from, to));
48                 }
49
50                 private RangeHeaderValue (RangeHeaderValue source)
51                         : this ()
52                 {
53                         if (source.ranges != null) {
54                                 foreach (var item in source.ranges)
55                                         Ranges.Add (item);
56                         }
57                 }
58
59                 public ICollection<RangeItemHeaderValue> Ranges {
60                         get {
61                                 return ranges ?? (ranges = new List<RangeItemHeaderValue> ());
62                         }
63                 }
64
65                 public string Unit {
66                         get {
67                                 return unit;
68                         }
69                         set {
70                                 if (value == null)
71                                         throw new ArgumentNullException ("Unit");
72
73                                 Parser.Token.Check (value);
74
75                                 unit = value;
76                         }
77                 }
78
79                 object ICloneable.Clone ()
80                 {
81                         return new RangeHeaderValue (this);
82                 }
83
84                 public override bool Equals (object obj)
85                 {
86                         var source = obj as RangeHeaderValue;
87                         if (source == null)
88                                 return false;
89
90                         return string.Equals (source.Unit, Unit, StringComparison.OrdinalIgnoreCase) &&
91                                 source.ranges.SequenceEqual (ranges);
92                 }
93
94                 public override int GetHashCode ()
95                 {
96                         return Unit.ToLowerInvariant ().GetHashCode () ^ HashCodeCalculator.Calculate (ranges);
97                 }
98
99                 public static RangeHeaderValue Parse (string input)
100                 {
101                         RangeHeaderValue value;
102                         if (TryParse (input, out value))
103                                 return value;
104
105                         throw new FormatException (input);
106                 }
107
108                 public static bool TryParse (string input, out RangeHeaderValue parsedValue)
109                 {
110                         parsedValue = null;
111
112                         var lexer = new Lexer (input);
113                         var t = lexer.Scan ();
114                         if (t != Token.Type.Token)
115                                 return false;
116
117                         var value = new RangeHeaderValue ();
118                         value.unit = lexer.GetStringValue (t);
119
120                         t = lexer.Scan ();
121                         if (t != Token.Type.SeparatorEqual)
122                                 return false;
123
124                         bool token_read;
125                         do {
126                                 int? from = null, to = null;
127                                 int number;
128                                 token_read = false;
129
130                                 t = lexer.Scan ();
131                                 switch (t.Kind) {
132                                 case Token.Type.SeparatorDash:
133                                         t = lexer.Scan ();
134                                         if (!lexer.TryGetNumericValue (t, out number))
135                                                 return false;
136
137                                         to = number;
138                                         break;
139                                 case Token.Type.Token:
140                                         string s = lexer.GetStringValue (t);
141                                         var values = s.Split (new [] { '-' }, StringSplitOptions.RemoveEmptyEntries);
142                                         if (!int.TryParse (values[0], out number))
143                                                 return false;
144
145                                         switch (values.Length) {
146                                         case 1:
147                                                 t = lexer.Scan ();
148                                                 switch (t.Kind) {
149                                                 case Token.Type.SeparatorDash:
150                                                         from = number;
151
152                                                         t = lexer.Scan ();
153                                                         if (t != Token.Type.Token) {
154                                                                 token_read = true;
155                                                                 break;
156                                                         }
157
158                                                         if (!lexer.TryGetNumericValue (t, out number))
159                                                                 return false;
160
161                                                         to = number;
162                                                         if (to < from)
163                                                                 return false;
164
165                                                         break;
166                                                 default:
167                                                         return false;
168                                                 }
169                                                 break;
170                                         case 2:
171                                                 from = number;
172
173                                                 if (!int.TryParse (values[1], out number))
174                                                         return false;
175
176                                                 to = number;
177                                                 if (to < from)
178                                                         return false;
179
180                                                 break;
181                                         default:
182                                                 return false;
183                                         }
184
185                                         break;
186                                 default:
187                                         return false;
188                                 }
189
190                                 value.Ranges.Add (new RangeItemHeaderValue (from, to));
191                                 if (!token_read)
192                                         t = lexer.Scan ();
193
194                         } while (t == Token.Type.SeparatorComma);
195
196                         if (t != Token.Type.End)
197                                 return false;
198
199                         parsedValue = value;
200                         return true;
201                 }
202
203                 public override string ToString ()
204                 {
205                         var sb = new StringBuilder (unit);
206                         sb.Append ("=");
207                         for (int i = 0; i < Ranges.Count; ++i) {
208                                 if (i > 0)
209                                         sb.Append (", ");
210
211                                 sb.Append (ranges[i]);
212                         }
213
214                         return sb.ToString ();
215                 }
216         }
217 }