PageRenderTime 27ms CodeModel.GetById 11ms app.highlight 12ms RepoModel.GetById 1ms app.codeStats 1ms

/WCFWebApi/src/System.Net.Http/System/Net/Http/Headers/RangeItemHeaderValue.cs

#
C# | 235 lines | 179 code | 41 blank | 15 comment | 57 complexity | 432100f21814e857065b720715f2ac5f MD5 | raw file
  1using System.Collections.Generic;
  2using System.Diagnostics.Contracts;
  3using System.Globalization;
  4
  5namespace System.Net.Http.Headers
  6{
  7    public class RangeItemHeaderValue : ICloneable
  8    {
  9        private long? from;
 10        private long? to;
 11
 12        public long? From
 13        {
 14            get { return from; }
 15        }
 16
 17        public long? To
 18        {
 19            get { return to; }
 20        }
 21
 22        public RangeItemHeaderValue(long? from, long? to)
 23        {
 24            if (!from.HasValue && !to.HasValue)
 25            {
 26                throw new ArgumentException(SR.net_http_headers_invalid_range);
 27            }
 28            if (from.HasValue && (from.Value < 0))
 29            {
 30                throw new ArgumentOutOfRangeException("from");
 31            }
 32            if (to.HasValue && (to.Value < 0))
 33            {
 34                throw new ArgumentOutOfRangeException("to");
 35            }
 36            if (from.HasValue && to.HasValue && (from.Value > to.Value))
 37            {
 38                throw new ArgumentOutOfRangeException("from");
 39            }
 40
 41            this.from = from;
 42            this.to = to;
 43        }
 44
 45        private RangeItemHeaderValue(RangeItemHeaderValue source)
 46        {
 47            Contract.Requires(source != null);
 48
 49            this.from = source.from;
 50            this.to = source.to;
 51        }
 52
 53        public override string ToString()
 54        {
 55            if (!from.HasValue)
 56            {
 57                return "-" + to.Value.ToString(NumberFormatInfo.InvariantInfo);
 58            }
 59            else if (!to.HasValue)
 60            {
 61                return from.Value.ToString(NumberFormatInfo.InvariantInfo) + "-";
 62            }
 63            return from.Value.ToString(NumberFormatInfo.InvariantInfo) + "-" +
 64                to.Value.ToString(NumberFormatInfo.InvariantInfo);
 65        }
 66
 67        public override bool Equals(object obj)
 68        {
 69            RangeItemHeaderValue other = obj as RangeItemHeaderValue;
 70
 71            if (other == null)
 72            {
 73                return false;
 74            }
 75            return ((from == other.from) && (to == other.to));
 76        }
 77
 78        public override int GetHashCode()
 79        {
 80            if (!from.HasValue)
 81            {
 82                return to.GetHashCode();
 83            }
 84            else if (!to.HasValue)
 85            {
 86                return from.GetHashCode();
 87            }
 88            return from.GetHashCode() ^ to.GetHashCode();
 89        }
 90
 91        // Returns the length of a range list. E.g. "1-2, 3-4, 5-6" adds 3 ranges to 'rangeCollection'. Note that empty
 92        // list segments are allowed, e.g. ",1-2, , 3-4,,".
 93        internal static int GetRangeItemListLength(string input, int startIndex,
 94            ICollection<RangeItemHeaderValue> rangeCollection)
 95        {
 96            Contract.Requires(rangeCollection != null);
 97            Contract.Requires(startIndex >= 0);
 98            Contract.Ensures((Contract.Result<int>() == 0) || (rangeCollection.Count > 0), 
 99                "If we can parse the string, then we expect to have at least one range item.");
100
101            if ((string.IsNullOrEmpty(input)) || (startIndex >= input.Length))
102            {
103                return 0;
104            }
105
106            // Empty segments are allowed, so skip all delimiter-only segments (e.g. ", ,").
107            bool separatorFound = false;
108            int current = HeaderUtilities.GetNextNonEmptyOrWhitespaceIndex(input, startIndex, true, out separatorFound);
109            // It's OK if we didn't find leading separator characters. Ignore 'separatorFound'.
110
111            if (current == input.Length)
112            {
113                return 0;
114            }
115
116            RangeItemHeaderValue range = null;
117            while (true)
118            {
119                int rangeLength = GetRangeItemLength(input, current, out range);
120
121                if (rangeLength == 0)
122                {
123                    return 0;
124                }
125
126                rangeCollection.Add(range);
127
128                current = current + rangeLength;
129                current = HeaderUtilities.GetNextNonEmptyOrWhitespaceIndex(input, current, true, out separatorFound);
130
131                // If the string is not consumed, we must have a delimiter, otherwise the string is not a valid 
132                // range list.
133                if ((current < input.Length) && !separatorFound)
134                {
135                    return 0;
136                }
137
138                if (current == input.Length)
139                {
140                    return current - startIndex;
141                }
142            }
143        }
144        
145        internal static int GetRangeItemLength(string input, int startIndex, out RangeItemHeaderValue parsedValue)
146        {
147            Contract.Requires(startIndex >= 0);
148
149            // This parser parses number ranges: e.g. '1-2', '1-', '-2'.
150
151            parsedValue = null;
152
153            if (string.IsNullOrEmpty(input) || (startIndex >= input.Length))
154            {
155                return 0;
156            }
157
158            // Caller must remove leading whitespaces. If not, we'll return 0.
159            int current = startIndex;
160
161            // Try parse the first value of a value pair.
162            int fromStartIndex = current;
163            int fromLength = HttpRuleParser.GetNumberLength(input, current, false);
164
165            if (fromLength > HttpRuleParser.MaxInt64Digits)
166            {
167                return 0;
168            }
169
170            current = current + fromLength;
171            current = current + HttpRuleParser.GetWhitespaceLength(input, current);
172
173            // Afer the first value, the '-' character must follow.
174            if ((current == input.Length) || (input[current] != '-'))
175            {
176                // We need a '-' character otherwise this can't be a valid range.
177                return 0;
178            }
179
180            current++; // skip the '-' character
181            current = current + HttpRuleParser.GetWhitespaceLength(input, current);
182
183            int toStartIndex = current;
184            int toLength = 0;
185
186            // If we didn't reach the end of the string, try parse the second value of the range.
187            if (current < input.Length)
188            {
189                toLength = HttpRuleParser.GetNumberLength(input, current, false);
190                
191                if (toLength > HttpRuleParser.MaxInt64Digits)
192                {
193                    return 0;
194                }
195                
196                current = current + toLength;
197                current = current + HttpRuleParser.GetWhitespaceLength(input, current);
198            }
199
200            if ((fromLength == 0) && (toLength == 0))
201            {
202                return 0; // At least one value must be provided in order to be a valid range.
203            }
204
205            // Try convert first value to int64
206            long from = 0;
207            if ((fromLength > 0) && !HeaderUtilities.TryParseInt64(input.Substring(fromStartIndex, fromLength), out from))
208            {
209                return 0;
210            }
211
212            // Try convert second value to int64
213            long to = 0;
214            if ((toLength > 0) && !HeaderUtilities.TryParseInt64(input.Substring(toStartIndex, toLength), out to))
215            {
216                return 0;
217            }
218
219            // 'from' must not be greater than 'to'
220            if ((fromLength > 0) && (toLength > 0) && (from > to))
221            {
222                return 0;
223            }
224
225            parsedValue = new RangeItemHeaderValue((fromLength == 0 ? (long?)null : (long?)from),
226                (toLength == 0 ? (long?)null : (long?)to));
227            return current - startIndex;
228        }
229
230        object ICloneable.Clone()
231        {
232            return new RangeItemHeaderValue(this);
233        }
234    }
235}