2 // Internal UriParseComponents class
5 // Vinicius Jarina <vinicius.jarina@xamarin.com>
7 // Copyright (C) 2012 Xamarin, Inc (http://www.xamarin.com)
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
34 internal class ParserState
36 public ParserState (string uri)
39 elements = new UriElements ();
42 public string remaining;
43 public UriElements elements;
46 // Parse Uri components (scheme, userinfo, host, query, fragment)
47 // http://www.ietf.org/rfc/rfc3986.txt
48 internal static class UriParseComponents
50 public static UriElements ParseComponents (string uri)
52 ParserState state = new ParserState (uri);
54 bool ok = ParseScheme (ref state);
56 ok = ParseAuthority (ref state);
58 ok = ParsePath (ref state);
60 ok = ParseQuery (ref state);
62 ParseFragment (ref state);
64 return state.elements;
67 private static bool IsAlpha (char ch)
69 return (('a' <= ch) && (ch <= 'z')) ||
70 (('A' <= ch) && (ch <= 'Z'));
73 // 3.1) scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
74 private static bool ParseScheme (ref ParserState state)
76 string part = state.remaining;
78 if (!IsAlpha (part [0]))
79 return part.Length > 0;
81 StringBuilder sb = new StringBuilder ();
85 for (index = 1; index < part.Length; index++ ) {
86 char ch = part [index];
87 if (ch != '.' && ch != '-' && ch != '+' && !IsAlpha (ch))
93 if (index + 1 <= part.Length && part [index] == ':') {
94 state.elements.scheme = sb.ToString ();
95 state.remaining = part.Substring (index + 1);
98 return state.remaining.Length > 0;
101 private static bool ParseAuthority (ref ParserState state)
103 string part = state.remaining;
105 if (part.Length < 2 || part [0] != '/' || part [1] != '/')
106 return part.Length > 0;
108 state.remaining = part.Substring (2);
110 bool ok = ParseUser (ref state);
112 ok = ParseHost (ref state);
114 ok = ParsePort (ref state);
118 static bool IsUnreserved (char ch)
120 return ch == '-' || ch == '.' || ch == '_' || ch == '~';
124 static bool IsSubDelim (char ch)
126 return ch == '!' || ch == '$' || ch == '&' || ch == '\'' || ch == '(' || ch == ')' ||
127 ch == '*' || ch == '+' || ch == ',' || ch == ';' || ch == '=';
130 // userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
131 private static bool ParseUser (ref ParserState state)
133 string part = state.remaining;
134 StringBuilder sb = null;
137 for (index = 0; index < part.Length; index++) {
138 char ch = part [index];
141 if (!Uri.IsHexEncoding (part, index))
143 ch = Uri.HexUnescape (part, ref index);
146 if (Char.IsLetterOrDigit (ch) || IsUnreserved (ch) || IsSubDelim (ch) || ch == ':'){
148 sb = new StringBuilder ();
154 if (index + 1 <= part.Length && part [index] == '@') {
155 state.elements.user = sb == null ? "" : sb.ToString ();
156 state.remaining = state.remaining.Substring (index + 1);
159 return state.remaining.Length > 0;
162 // host = IP-literal / IPv4address / reg-name
163 private static bool ParseHost (ref ParserState state)
165 string part = state.remaining;
166 StringBuilder sb = new StringBuilder ();
169 for (index = 0; index < part.Length; index++) {
171 char ch = part [index];
173 if (ch == '/' || ch == ':' || ch == '#' || ch == '?')
179 if (index <= part.Length)
180 state.remaining = part.Substring (index);
182 state.elements.host = sb.ToString();
184 return state.remaining.Length > 0;
188 private static bool ParsePort (ref ParserState state)
190 string part = state.remaining;
191 if (part.Length == 0 || part [0] != ':')
192 return part.Length > 0;
194 StringBuilder sb = new StringBuilder ();
197 for (index = 1; index < part.Length; index++ ) {
198 char ch = part [index];
200 if (!char.IsDigit (ch))
206 if (index <= part.Length)
207 state.remaining = part.Substring (index);
209 state.elements.port = sb.ToString();
211 return state.remaining.Length > 0;
214 private static bool ParsePath (ref ParserState state)
216 string part = state.remaining;
217 StringBuilder sb = new StringBuilder ();
220 for (index = 0; index < part.Length; index++) {
222 char ch = part [index];
224 if (ch == '#' || ch == '?')
230 if (index <= part.Length)
231 state.remaining = part.Substring (index);
233 state.elements.path = sb.ToString ();
235 return state.remaining.Length > 0;
238 private static bool ParseQuery (ref ParserState state)
240 string part = state.remaining;
242 if (part.Length == 0 || part [0] != '?')
243 return part.Length > 0;
245 StringBuilder sb = new StringBuilder ();
248 for (index = 1; index < part.Length; index++) {
250 char ch = part [index];
258 if (index <= part.Length)
259 state.remaining = part.Substring (index);
261 state.elements.query = sb.ToString ();
263 return state.remaining.Length > 0;
266 private static bool ParseFragment (ref ParserState state)
268 string part = state.remaining;
270 if (part.Length == 0 || part [0] != '#')
271 return part.Length > 0;
273 StringBuilder sb = new StringBuilder ();
276 for (index = 1; index < part.Length; index++) {
278 char ch = part [index];
283 state.elements.fragment = sb.ToString ();