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 // userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
119 private static bool ParseUser (ref ParserState state)
121 string part = state.remaining;
122 StringBuilder sb = new StringBuilder ();
125 for (index = 0; index < part.Length; index++) {
127 char ch = part [index];
129 if (ch == '@' || ch == '/' && ch == '#' && ch == '?')
135 if (index + 1 <= part.Length && part [index] == '@') {
137 state.elements.user = sb.ToString ();
138 state.remaining = state.remaining.Substring (index + 1);
141 return state.remaining.Length > 0;
144 // host = IP-literal / IPv4address / reg-name
145 private static bool ParseHost (ref ParserState state)
147 string part = state.remaining;
148 StringBuilder sb = new StringBuilder ();
151 for (index = 0; index < part.Length; index++) {
153 char ch = part [index];
155 if (ch == '/' || ch == ':' || ch == '#' || ch == '?')
161 if (index <= part.Length)
162 state.remaining = part.Substring (index);
164 state.elements.host = sb.ToString();
166 return state.remaining.Length > 0;
170 private static bool ParsePort (ref ParserState state)
172 string part = state.remaining;
173 if (part.Length == 0 || part [0] != ':')
174 return part.Length > 0;
176 StringBuilder sb = new StringBuilder ();
179 for (index = 1; index < part.Length; index++ ) {
180 char ch = part [index];
182 if (!char.IsDigit (ch))
188 if (index <= part.Length)
189 state.remaining = part.Substring (index);
191 state.elements.port = sb.ToString();
193 return state.remaining.Length > 0;
196 private static bool ParsePath (ref ParserState state)
198 string part = state.remaining;
199 StringBuilder sb = new StringBuilder ();
202 for (index = 0; index < part.Length; index++) {
204 char ch = part [index];
206 if (ch == '#' || ch == '?')
212 if (index <= part.Length)
213 state.remaining = part.Substring (index);
215 state.elements.path = sb.ToString ();
217 return state.remaining.Length > 0;
220 private static bool ParseQuery (ref ParserState state)
222 string part = state.remaining;
224 if (part.Length == 0 || part [0] != '?')
225 return part.Length > 0;
227 StringBuilder sb = new StringBuilder ();
230 for (index = 1; index < part.Length; index++) {
232 char ch = part [index];
240 if (index <= part.Length)
241 state.remaining = part.Substring (index);
243 state.elements.query = sb.ToString ();
245 return state.remaining.Length > 0;
248 private static bool ParseFragment (ref ParserState state)
250 string part = state.remaining;
252 if (part.Length == 0 || part [0] != '#')
253 return part.Length > 0;
255 StringBuilder sb = new StringBuilder ();
258 for (index = 1; index < part.Length; index++) {
260 char ch = part [index];
265 state.elements.fragment = sb.ToString ();