KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sslexplorer > agent > client > util > URI


1 /*
2  * The Apache Software License, Version 1.1
3  *
4  *
5  * Copyright (c) 1999 The Apache Software Foundation. All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
8  * conditions are met:
9  * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
11  * disclaimer in the documentation and/or other materials provided with the distribution.
12  * 3. The end-user documentation included with the redistribution, if any, must include the following acknowledgment: "This
13  * product includes software developed by the Apache Software Foundation (http://www.apache.org/)." Alternately, this
14  * acknowledgment may appear in the software itself, if and wherever such third-party acknowledgments normally appear.
15  * 4. The names "Xalan" and "Apache Software Foundation" must not be used to endorse or promote products derived from this
16  * software without prior written permission. For written permission, please contact apache@apache.org.
17  * 5. Products derived from this software may not be called "Apache", nor may "Apache" appear in their name, without prior written
18  * permission of the Apache Software Foundation.
19  *
20  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE
22  * FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  * ====================================================================
27  *
28  * This software consists of voluntary contributions made by many individuals on behalf of the Apache Software Foundation and was
29  * originally based on software copyright (c) 1999, Lotus Development Corporation., http://www.lotus.com. For more information on
30  * the Apache Software Foundation, please see <http://www.apache.org/> .
31  *
32  *
33  *
34  * Adapted for use as a standalone class in Maverick by removing references to XSLMessages and XSLErrorResources.
35  */

36 package com.sslexplorer.agent.client.util;
37
38 import java.io.IOException JavaDoc;
39 import java.io.Serializable JavaDoc;
40 import java.text.MessageFormat JavaDoc;
41
42 /**
43  * A class to represent a Uniform Resource Identifier (URI). This class is designed to handle the parsing of URIs and provide
44  * access to the various components (scheme, host, port, userinfo, path, query string and fragment) that may constitute a URI.
45  * <p>
46  * Parsing of a URI specification is done according to the URI syntax described in RFC 2396
47  * <http://www.ietf.org/rfc/rfc2396.txt?number=2396>. Every URI consists of a scheme, followed by a colon (':'), followed by a
48  * scheme-specific part. For URIs that follow the "generic URI" syntax, the scheme- specific part begins with two slashes ("//")
49  * and may be followed by an authority segment (comprised of user information, host, and port), path segment, query segment and
50  * fragment. Note that RFC 2396 no longer specifies the use of the parameters segment and excludes the "user:password" syntax as
51  * part of the authority segment. If "user:password" appears in a URI, the entire user/password string is stored as userinfo.
52  * <p>
53  * For URIs that do not follow the "generic URI" syntax (e.g. mailto), the entire scheme-specific part is treated as the "path"
54  * portion of the URI.
55  * <p>
56  * Note that, unlike the java.net.URL class, this class does not provide any built-in network access functionality nor does it
57  * provide any scheme-specific functionality (for example, it does not know a default port for a specific scheme). Rather, it only
58  * knows the grammar and basic set of operations that can be applied to a URI.
59  *
60  *
61  */

62 public class URI
63     implements Serializable JavaDoc {
64
65   /**
66    * MalformedURIExceptions are thrown in the process of building a URI or setting fields on a URI when an operation would result
67    * in an invalid URI specification.
68    *
69    */

70   public static class MalformedURIException
71       extends IOException JavaDoc {
72
73     /**
74      * Constructs a <code>MalformedURIException</code> with no specified detail message.
75      */

76     public MalformedURIException() {
77       super();
78     }
79
80     /**
81      * Constructs a <code>MalformedURIException</code> with the specified detail message.
82      *
83      * @param p_msg the detail message.
84      */

85     public MalformedURIException(String JavaDoc p_msg) {
86       super(p_msg);
87     }
88   }
89
90   /** reserved characters */
91   private static final String JavaDoc RESERVED_CHARACTERS = ";/?:@&=+$,"; //$NON-NLS-1$
92

93   /**
94    * URI punctuation mark characters - these, combined with alphanumerics, constitute the "unreserved" characters
95    */

96   private static final String JavaDoc MARK_CHARACTERS = "-_.!~*'() "; //$NON-NLS-1$
97

98   /** scheme can be composed of alphanumerics and these characters */
99   private static final String JavaDoc SCHEME_CHARACTERS = "+-."; //$NON-NLS-1$
100

101   /**
102    * userinfo can be composed of unreserved, escaped and these characters
103    */

104   private static final String JavaDoc USERINFO_CHARACTERS = ";:&=+$,"; //$NON-NLS-1$
105

106   /**
107    * Stores the scheme (usually the protocol) for this URI.
108    *
109    * @serial
110    */

111   private String JavaDoc m_scheme = null;
112
113   /**
114    * If specified, stores the userinfo for this URI; otherwise null.
115    *
116    * @serial
117    */

118   private String JavaDoc m_userinfo = null;
119
120   /**
121    * If specified, stores the host for this URI; otherwise null.
122    *
123    * @serial
124    */

125   private String JavaDoc m_host = null;
126
127   /**
128    * If specified, stores the port for this URI; otherwise -1.
129    *
130    * @serial
131    */

132   private int m_port = -1;
133
134   /**
135    * If specified, stores the path for this URI; otherwise null.
136    *
137    * @serial
138    */

139   private String JavaDoc m_path = null;
140
141   /**
142    * If specified, stores the query string for this URI; otherwise null.
143    *
144    * @serial
145    */

146   private String JavaDoc m_queryString = null;
147
148   /**
149    * If specified, stores the fragment for this URI; otherwise null.
150    *
151    * @serial
152    */

153   private String JavaDoc m_fragment = null;
154
155   /**
156    * Construct a new and uninitialized URI.
157    */

158   public URI() {
159   }
160
161   /**
162    * Construct a new URI from another URI. All fields for this URI are set equal to the fields of the URI passed in.
163    *
164    * @param p_other the URI to copy (cannot be null)
165    */

166   public URI(URI p_other) {
167     initialize(p_other);
168   }
169
170   /**
171    * Construct a new URI from a URI specification string. If the specification follows the "generic URI" syntax, (two slashes
172    * following the first colon), the specification will be parsed accordingly - setting the scheme, userinfo, host,port, path,
173    * query string and fragment fields as necessary. If the specification does not follow the "generic URI" syntax, the
174    * specification is parsed into a scheme and scheme-specific part (stored as the path) only.
175    *
176    * @param p_uriSpec the URI specification string (cannot be null or empty)
177    *
178    * @throws MalformedURIException if p_uriSpec violates any syntax rules
179    */

180   public URI(String JavaDoc p_uriSpec) throws MalformedURIException {
181     this( (URI)null, p_uriSpec);
182   }
183
184   /**
185    * Construct a new URI from a base URI and a URI specification string. The URI specification string may be a relative URI.
186    *
187    * @param p_base the base URI (cannot be null if p_uriSpec is null or empty)
188    * @param p_uriSpec the URI specification string (cannot be null or empty if p_base is null)
189    *
190    * @throws MalformedURIException if p_uriSpec violates any syntax rules
191    */

192   public URI(URI p_base, String JavaDoc p_uriSpec) throws MalformedURIException {
193     initialize(p_base, p_uriSpec);
194   }
195
196   /**
197    * Construct a new URI that does not follow the generic URI syntax. Only the scheme and scheme-specific part (stored as the
198    * path) are initialized.
199    *
200    * @param p_scheme the URI scheme (cannot be null or empty)
201        * @param p_schemeSpecificPart the scheme-specific part (cannot be null or empty)
202    *
203    * @throws MalformedURIException if p_scheme violates any syntax rules
204    */

205   public URI(String JavaDoc p_scheme, String JavaDoc p_schemeSpecificPart) throws
206       MalformedURIException {
207
208     if (p_scheme == null || p_scheme.trim().length() == 0) {
209       throw new MalformedURIException(
210           Messages.getString("URI.cannotConstructURIWithNullEmptyScheme")); //$NON-NLS-1$
211
}
212
213     if (p_schemeSpecificPart == null ||
214         p_schemeSpecificPart.trim().length() == 0) {
215       throw new MalformedURIException(
216           Messages.getString("URI.cannotConstructURIWIthNullEmptySchemeSpecificPart")); //$NON-NLS-1$
217
}
218
219     setScheme(p_scheme);
220     setPath(p_schemeSpecificPart);
221   }
222
223   /**
224    * Construct a new URI that follows the generic URI syntax from its component parts. Each component is validated for syntax and
225    * some basic semantic checks are performed as well. See the individual setter methods for specifics.
226    *
227    * @param p_scheme the URI scheme (cannot be null or empty)
228    * @param p_host the hostname or IPv4 address for the URI
229    * @param p_path the URI path - if the path contains '?' or '#', then the query string and/or fragment will be set from the
230    * path; however, if the query and fragment are specified both in the path and as separate parameters, an exception
231    * is thrown
232    * @param p_queryString the URI query string (cannot be specified if path is null)
233    * @param p_fragment the URI fragment (cannot be specified if path is null)
234    *
235    * @throws MalformedURIException if any of the parameters violates syntax rules or semantic rules
236    */

237   public URI(String JavaDoc p_scheme, String JavaDoc p_host, String JavaDoc p_path,
238              String JavaDoc p_queryString, String JavaDoc p_fragment) throws
239       MalformedURIException {
240     this(p_scheme, null, p_host, -1, p_path, p_queryString, p_fragment);
241   }
242
243   /**
244    * Construct a new URI that follows the generic URI syntax from its component parts. Each component is validated for syntax and
245    * some basic semantic checks are performed as well. See the individual setter methods for specifics.
246    *
247    * @param p_scheme the URI scheme (cannot be null or empty)
248    * @param p_userinfo the URI userinfo (cannot be specified if host is null)
249    * @param p_host the hostname or IPv4 address for the URI
250    * @param p_port the URI port (may be -1 for "unspecified"; cannot be specified if host is null)
251    * @param p_path the URI path - if the path contains '?' or '#', then the query string and/or fragment will be set from the
252    * path; however, if the query and fragment are specified both in the path and as separate parameters, an exception
253    * is thrown
254    * @param p_queryString the URI query string (cannot be specified if path is null)
255    * @param p_fragment the URI fragment (cannot be specified if path is null)
256    *
257    * @throws MalformedURIException if any of the parameters violates syntax rules or semantic rules
258    */

259   public URI(
260       String JavaDoc p_scheme,
261       String JavaDoc p_userinfo,
262       String JavaDoc p_host,
263       int p_port,
264       String JavaDoc p_path,
265       String JavaDoc p_queryString,
266       String JavaDoc p_fragment) throws MalformedURIException {
267
268     if (p_scheme == null || p_scheme.trim().length() == 0) {
269       throw new MalformedURIException(Messages.getString("URI.schemeIsRequired")); //$NON-NLS-1$
270
}
271
272     if (p_host == null) {
273       if (p_userinfo != null) {
274         throw new MalformedURIException(
275             Messages.getString("URI.userinfoMayNotBeSpecirfiedIfHostIsNotSpecified")); //$NON-NLS-1$
276
}
277
278       if (p_port != -1) {
279         throw new MalformedURIException(
280             Messages.getString("URI.portMayNotBeSpecifiedIfHostIsNotSpecified")); //$NON-NLS-1$
281
}
282     }
283
284     if (p_path != null) {
285       if (p_path.indexOf('?') != -1 && p_queryString != null) {
286         throw new MalformedURIException(
287             Messages.getString("URI.queryStringCannotBeSpecifiedInPathAndQueryString")); //$NON-NLS-1$
288
}
289
290       if (p_path.indexOf('#') != -1 && p_fragment != null) {
291         throw new MalformedURIException(
292             Messages.getString("URI.fragmentCannotBeSpecifiedInBothPathAndFragment")); //$NON-NLS-1$
293
}
294     }
295
296     setScheme(p_scheme);
297     setHost(p_host);
298     setPort(p_port);
299     setUserinfo(p_userinfo);
300     setPath(p_path);
301     setQueryString(p_queryString);
302     setFragment(p_fragment);
303   }
304
305   /**
306    * Initialize all fields of this URI from another URI.
307    *
308    * @param p_other the URI to copy (cannot be null)
309    */

310   private void initialize(URI p_other) {
311
312     m_scheme = p_other.getScheme();
313     m_userinfo = p_other.getUserinfo();
314     m_host = p_other.getHost();
315     m_port = p_other.getPort();
316     m_path = p_other.getPath();
317     m_queryString = p_other.getQueryString();
318     m_fragment = p_other.getFragment();
319   }
320
321   /**
322    * Initializes this URI from a base URI and a URI specification string. See RFC 2396 Section 4 and Appendix B for
323    * specifications on parsing the URI and Section 5 for specifications on resolving relative URIs and relative paths.
324    *
325    * @param p_base the base URI (may be null if p_uriSpec is an absolute URI)
326    * @param p_uriSpec the URI spec string which may be an absolute or relative URI (can only be null/empty if p_base is not null)
327    *
328    * @throws MalformedURIException if p_base is null and p_uriSpec is not an absolute URI or if p_uriSpec violates syntax rules
329    */

330   private void initialize(URI p_base, String JavaDoc p_uriSpec) throws
331       MalformedURIException {
332
333     if (p_base == null && (p_uriSpec == null || p_uriSpec.trim().length() == 0)) {
334       throw new MalformedURIException(
335           Messages.getString("URI.cannotInitialiseURIWithEmptyParameters")); //$NON-NLS-1$
336
}
337
338     // just make a copy of the base if spec is empty
339
if (p_uriSpec == null || p_uriSpec.trim().length() == 0) {
340       initialize(p_base);
341
342       return;
343     }
344
345     String JavaDoc uriSpec = p_uriSpec.trim();
346     int uriSpecLen = uriSpec.length();
347     int index = 0;
348
349     // check for scheme
350
if (uriSpec.indexOf(':') == -1) {
351       if (p_base == null) {
352         throw new MalformedURIException(MessageFormat.format(Messages.getString("URI.noSchemeFoundInURI"), new Object JavaDoc[] { uriSpec } ) ); //$NON-NLS-1$
353
}
354     }
355     else {
356       initializeScheme(uriSpec);
357
358       index = m_scheme.length() + 1;
359     }
360
361     // two slashes means generic URI syntax, so we get the authority
362
if ( ( (index + 1) < uriSpecLen) &&
363         (uriSpec.substring(index).startsWith("//"))) { //$NON-NLS-1$
364
index += 2;
365
366       int startPos = index;
367
368       // get authority - everything up to path, query or fragment
369
char testChar = '\0';
370
371       while (index < uriSpecLen) {
372         testChar = uriSpec.charAt(index);
373
374         if (testChar == '/' || testChar == '?' || testChar == '#') {
375           break;
376         }
377
378         index++;
379       }
380
381       // if we found authority, parse it out, otherwise we set the
382
// host to empty string
383
if (index > startPos) {
384         initializeAuthority(uriSpec.substring(startPos, index));
385       }
386       else {
387         m_host = ""; //$NON-NLS-1$
388
}
389     }
390
391     initializePath(uriSpec.substring(index));
392
393     // Resolve relative URI to base URI - see RFC 2396 Section 5.2
394
// In some cases, it might make more sense to throw an exception
395
// (when scheme is specified is the string spec and the base URI
396
// is also specified, for example), but we're just following the
397
// RFC specifications
398
if (p_base != null) {
399
400       // check to see if this is the current doc - RFC 2396 5.2 #2
401
// note that this is slightly different from the RFC spec in that
402
// we don't include the check for query string being null
403
// - this handles cases where the urispec is just a query
404
// string or a fragment (e.g. "?y" or "#s") -
405
// see <http://www.ics.uci.edu/~fielding/url/test1.html> which
406
// identified this as a bug in the RFC
407
if (m_path.length() == 0 && m_scheme == null && m_host == null) {
408         m_scheme = p_base.getScheme();
409         m_userinfo = p_base.getUserinfo();
410         m_host = p_base.getHost();
411         m_port = p_base.getPort();
412         m_path = p_base.getPath();
413
414         if (m_queryString == null) {
415           m_queryString = p_base.getQueryString();
416         }
417
418         return;
419       }
420
421       // check for scheme - RFC 2396 5.2 #3
422
// if we found a scheme, it means absolute URI, so we're done
423
if (m_scheme == null) {
424         m_scheme = p_base.getScheme();
425       }
426       else {
427         return;
428       }
429
430       // check for authority - RFC 2396 5.2 #4
431
// if we found a host, then we've got a network path, so we're done
432
if (m_host == null) {
433         m_userinfo = p_base.getUserinfo();
434         m_host = p_base.getHost();
435         m_port = p_base.getPort();
436       }
437       else {
438         return;
439       }
440
441       // check for absolute path - RFC 2396 5.2 #5
442
if (m_path.length() > 0 && m_path.startsWith("/")) { //$NON-NLS-1$
443
return;
444       }
445
446       // if we get to this point, we need to resolve relative path
447
// RFC 2396 5.2 #6
448
String JavaDoc path = new String JavaDoc();
449       String JavaDoc basePath = p_base.getPath();
450
451       // 6a - get all but the last segment of the base URI path
452
if (basePath != null) {
453         int lastSlash = basePath.lastIndexOf('/');
454
455         if (lastSlash != -1) {
456           path = basePath.substring(0, lastSlash + 1);
457         }
458       }
459
460       // 6b - append the relative URI path
461
path = path.concat(m_path);
462
463       // 6c - remove all "./" where "." is a complete path segment
464
index = -1;
465
466       while ( (index = path.indexOf("/./")) != -1) { //$NON-NLS-1$
467
path = path.substring(0, index + 1).concat(path.substring(index + 3));
468       }
469
470       // 6d - remove "." if path ends with "." as a complete path segment
471
if (path.endsWith("/.")) { //$NON-NLS-1$
472
path = path.substring(0, path.length() - 1);
473       }
474
475       // 6e - remove all "<segment>/../" where "<segment>" is a complete
476
// path segment not equal to ".."
477
index = -1;
478
479       int segIndex = -1;
480       String JavaDoc tempString = null;
481
482       while ( (index = path.indexOf("/../")) > 0) { //$NON-NLS-1$
483
tempString = path.substring(0, path.indexOf("/../")); //$NON-NLS-1$
484
segIndex = tempString.lastIndexOf('/');
485
486         if (segIndex != -1) {
487           if (!tempString.substring(segIndex++).equals("..")) { //$NON-NLS-1$
488
path = path.substring(0, segIndex).concat(path.substring(index + 4));
489           }
490         }
491       }
492
493       // 6f - remove ending "<segment>/.." where "<segment>" is a
494
// complete path segment
495
if (path.endsWith("/..")) { //$NON-NLS-1$
496
tempString = path.substring(0, path.length() - 3);
497         segIndex = tempString.lastIndexOf('/');
498
499         if (segIndex != -1) {
500           path = path.substring(0, segIndex + 1);
501         }
502       }
503
504       m_path = path;
505     }
506   }
507
508   /**
509    * Initialize the scheme for this URI from a URI string spec.
510    *
511    * @param p_uriSpec the URI specification (cannot be null)
512    *
513    * @throws MalformedURIException if URI does not have a conformant scheme
514    */

515   private void initializeScheme(String JavaDoc p_uriSpec) throws MalformedURIException {
516
517     int uriSpecLen = p_uriSpec.length();
518     int index = 0;
519     String JavaDoc scheme = null;
520     char testChar = '\0';
521
522     while (index < uriSpecLen) {
523       testChar = p_uriSpec.charAt(index);
524
525       if (testChar == ':' || testChar == '/' || testChar == '?' ||
526           testChar == '#') {
527         break;
528       }
529
530       index++;
531     }
532
533     scheme = p_uriSpec.substring(0, index);
534
535     if (scheme.length() == 0) {
536       throw new MalformedURIException(Messages.getString("URI.noSchemeFoundInURIShort")); //$NON-NLS-1$
537
}
538     else {
539       setScheme(scheme);
540     }
541   }
542
543   /**
544    * Initialize the authority (userinfo, host and port) for this URI from a URI string spec.
545    *
546    * @param p_uriSpec the URI specification (cannot be null)
547    *
548    * @throws MalformedURIException if p_uriSpec violates syntax rules
549    */

550   private void initializeAuthority(String JavaDoc p_uriSpec) throws
551       MalformedURIException {
552
553     int index = 0;
554     int start = 0;
555     int end = p_uriSpec.length();
556     char testChar = '\0';
557     String JavaDoc userinfo = null;
558
559     // userinfo is everything up @
560
if (p_uriSpec.indexOf('@', start) != -1) {
561       while (index < end) {
562         testChar = p_uriSpec.charAt(index);
563
564         if (testChar == '@') {
565           break;
566         }
567
568         index++;
569       }
570
571       userinfo = p_uriSpec.substring(start, index);
572
573       index++;
574     }
575
576     // host is everything up to ':'
577
String JavaDoc host = null;
578
579     start = index;
580
581     while (index < end) {
582       testChar = p_uriSpec.charAt(index);
583
584       if (testChar == ':') {
585         break;
586       }
587
588       index++;
589     }
590
591     host = p_uriSpec.substring(start, index);
592
593     int port = -1;
594
595     if (host.length() > 0) {
596
597       // port
598
if (testChar == ':') {
599         index++;
600
601         start = index;
602
603         while (index < end) {
604           index++;
605         }
606
607         String JavaDoc portStr = p_uriSpec.substring(start, index);
608
609         if (portStr.length() > 0) {
610           for (int i = 0; i < portStr.length(); i++) {
611             if (!isDigit(portStr.charAt(i))) {
612               throw new MalformedURIException(
613                   MessageFormat.format(Messages.getString("URI.portIsInvalid"), new Object JavaDoc[] { portStr } ) ); //$NON-NLS-1$
614
}
615           }
616
617           try {
618             port = Integer.parseInt(portStr);
619           }
620           catch (NumberFormatException JavaDoc nfe) {
621
622             // can't happen
623
}
624         }
625       }
626     }
627
628     setHost(host);
629     setPort(port);
630     setUserinfo(userinfo);
631   }
632
633   /**
634    * Initialize the path for this URI from a URI string spec.
635    *
636    * @param p_uriSpec the URI specification (cannot be null)
637    *
638    * @throws MalformedURIException if p_uriSpec violates syntax rules
639    */

640   private void initializePath(String JavaDoc p_uriSpec) throws MalformedURIException {
641
642     if (p_uriSpec == null) {
643       throw new MalformedURIException(
644           Messages.getString("URI.cannotInitialisePathFromNullString")); //$NON-NLS-1$
645
}
646
647     int index = 0;
648     int start = 0;
649     int end = p_uriSpec.length();
650     char testChar = '\0';
651
652     // path - everything up to query string or fragment
653
while (index < end) {
654       testChar = p_uriSpec.charAt(index);
655
656       if (testChar == '?' || testChar == '#') {
657         break;
658       }
659
660       // check for valid escape sequence
661
if (testChar == '%') {
662         if (index + 2 >= end || !isHex(p_uriSpec.charAt(index + 1)) ||
663             !isHex(p_uriSpec.charAt(index + 2))) {
664           throw new MalformedURIException(
665               Messages.getString("URI.pathContainsInvalidEscapeSequence")); //$NON-NLS-1$
666
}
667       }
668       else
669       if (!isReservedCharacter(testChar) && !isUnreservedCharacter(testChar)) {
670         if ('\\' != testChar) {
671           throw new MalformedURIException(MessageFormat.format(Messages.getString("URI.pathContainsInvalidCharacter"), new Object JavaDoc[] { String.valueOf(testChar) } ) ); //$NON-NLS-1$
672
}
673       }
674
675       index++;
676     }
677
678     m_path = p_uriSpec.substring(start, index);
679
680     // query - starts with ? and up to fragment or end
681
if (testChar == '?') {
682       index++;
683
684       start = index;
685
686       while (index < end) {
687         testChar = p_uriSpec.charAt(index);
688
689         if (testChar == '#') {
690           break;
691         }
692
693         if (testChar == '%') {
694           if (index + 2 >= end || !isHex(p_uriSpec.charAt(index + 1)) ||
695               !isHex(p_uriSpec.charAt(index + 2))) {
696             throw new MalformedURIException(
697                 Messages.getString("URI.queryContainsInvalidEscapeSequence")); //$NON-NLS-1$
698
}
699         }
700         else
701         if (!isReservedCharacter(testChar) && !isUnreservedCharacter(testChar)) {
702           throw new MalformedURIException(
703               MessageFormat.format(Messages.getString("URI.queryContainsInvalidCharacter"), new Object JavaDoc[] { String.valueOf(testChar) } ) ); //$NON-NLS-1$
704
}
705
706         index++;
707       }
708
709       m_queryString = p_uriSpec.substring(start, index);
710     }
711
712     // fragment - starts with #
713
if (testChar == '#') {
714       index++;
715
716       start = index;
717
718       while (index < end) {
719         testChar = p_uriSpec.charAt(index);
720
721         if (testChar == '%') {
722           if (index + 2 >= end || !isHex(p_uriSpec.charAt(index + 1)) ||
723               !isHex(p_uriSpec.charAt(index + 2))) {
724             throw new MalformedURIException(
725                 Messages.getString("URI.fragmentContainsInvalidEscapeSequence")); //$NON-NLS-1$
726
}
727         }
728         else
729         if (!isReservedCharacter(testChar) && !isUnreservedCharacter(testChar)) {
730           throw new MalformedURIException(
731               MessageFormat.format(Messages.getString("URI.fragmentContainsInvalidCharacter"), new Object JavaDoc[] { String.valueOf(testChar) } ) ); //$NON-NLS-1$
732
}
733
734         index++;
735       }
736
737       m_fragment = p_uriSpec.substring(start, index);
738     }
739   }
740
741   /**
742    * Get the scheme for this URI.
743    *
744    * @return the scheme for this URI
745    */

746   public String JavaDoc getScheme() {
747     return m_scheme;
748   }
749
750   /**
751    * Get the scheme-specific part for this URI (everything following the scheme and the first colon). See RFC 2396 Section 5.2
752    * for spec.
753    *
754    * @return the scheme-specific part for this URI
755    */

756   public String JavaDoc getSchemeSpecificPart() {
757
758     StringBuffer JavaDoc schemespec = new StringBuffer JavaDoc();
759
760     if (m_userinfo != null || m_host != null || m_port != -1) {
761       schemespec.append("//"); //$NON-NLS-1$
762
}
763
764     if (m_userinfo != null) {
765       schemespec.append(m_userinfo);
766       schemespec.append('@');
767     }
768
769     if (m_host != null) {
770       schemespec.append(m_host);
771     }
772
773     if (m_port != -1) {
774       schemespec.append(':');
775       schemespec.append(m_port);
776     }
777
778     if (m_path != null) {
779       schemespec.append( (m_path));
780     }
781
782     if (m_queryString != null) {
783       schemespec.append('?');
784       schemespec.append(m_queryString);
785     }
786
787     if (m_fragment != null) {
788       schemespec.append('#');
789       schemespec.append(m_fragment);
790     }
791
792     return schemespec.toString();
793   }
794
795   /**
796    * Get the userinfo for this URI.
797    *
798    * @return the userinfo for this URI (null if not specified).
799    */

800   public String JavaDoc getUserinfo() {
801     return m_userinfo;
802   }
803
804   /**
805    * Get the host for this URI.
806    *
807    * @return the host for this URI (null if not specified).
808    */

809   public String JavaDoc getHost() {
810     return m_host;
811   }
812
813   /**
814    * Get the port for this URI.
815    *
816    * @return the port for this URI (-1 if not specified).
817    */

818   public int getPort() {
819     return m_port;
820   }
821
822   /**
823    * Get the path for this URI (optionally with the query string and fragment).
824    *
825    * @param p_includeQueryString if true (and query string is not null), then a "?" followed by the query string will be appended
826    * @param p_includeFragment if true (and fragment is not null), then a "#" followed by the fragment will be appended
827    *
828        * @return the path for this URI possibly including the query string and fragment
829    */

830   public String JavaDoc getPath(boolean p_includeQueryString, boolean p_includeFragment) {
831
832     StringBuffer JavaDoc pathString = new StringBuffer JavaDoc(m_path);
833
834     if (p_includeQueryString && m_queryString != null) {
835       pathString.append('?');
836       pathString.append(m_queryString);
837     }
838
839     if (p_includeFragment && m_fragment != null) {
840       pathString.append('#');
841       pathString.append(m_fragment);
842     }
843
844     return pathString.toString();
845   }
846
847   /**
848    * Get the path for this URI. Note that the value returned is the path only and does not include the query string or fragment.
849    *
850    * @return the path for this URI.
851    */

852   public String JavaDoc getPath() {
853     return m_path;
854   }
855
856   /**
857    * Get the query string for this URI.
858    *
859    * @return the query string for this URI. Null is returned if there was no "?" in the URI spec, empty string if there was a "?"
860    * but no query string following it.
861    */

862   public String JavaDoc getQueryString() {
863     return m_queryString;
864   }
865
866   /**
867    * Get the fragment for this URI.
868    *
869    * @return the fragment for this URI. Null is returned if there was no "#" in the URI spec, empty string if there was a "#" but
870    * no fragment following it.
871    */

872   public String JavaDoc getFragment() {
873     return m_fragment;
874   }
875
876   /**
877    * Set the scheme for this URI. The scheme is converted to lowercase before it is set.
878    *
879    * @param p_scheme the scheme for this URI (cannot be null)
880    *
881    * @throws MalformedURIException if p_scheme is not a conformant scheme name
882    */

883   public void setScheme(String JavaDoc p_scheme) throws MalformedURIException {
884
885     if (p_scheme == null) {
886       throw new MalformedURIException(Messages.getString("URI.cannotSetSchemeFromNullString")); //$NON-NLS-1$
887
}
888
889     if (!isConformantSchemeName(p_scheme)) {
890       throw new MalformedURIException(Messages.getString("URI.theSchemeIsNonConformant")); //$NON-NLS-1$
891
}
892
893     m_scheme = p_scheme.toLowerCase();
894   }
895
896   /**
897    * Set the userinfo for this URI. If a non-null value is passed in and the host value is null, then an exception is thrown.
898    *
899    * @param p_userinfo the userinfo for this URI
900    *
901    * @throws MalformedURIException if p_userinfo contains invalid characters
902    */

903   public void setUserinfo(String JavaDoc p_userinfo) throws MalformedURIException {
904
905     if (p_userinfo == null) {
906       m_userinfo = null;
907     }
908     else {
909       if (m_host == null) {
910         throw new MalformedURIException(
911             Messages.getString("URI.userinfoCannotBeSetWhenHostIsNull")); //$NON-NLS-1$
912
}
913
914       // userinfo can contain alphanumerics, mark characters, escaped
915
// and ';',':','&','=','+','$',','
916
int index = 0;
917       int end = p_userinfo.length();
918       char testChar = '\0';
919
920       while (index < end) {
921         testChar = p_userinfo.charAt(index);
922
923         if (testChar == '%') {
924           if (index + 2 >= end || !isHex(p_userinfo.charAt(index + 1)) ||
925               !isHex(p_userinfo.charAt(index + 2))) {
926             throw new MalformedURIException(
927                 Messages.getString("URI.userinfoContainsInvalidEscapeSequence")); //$NON-NLS-1$
928
}
929         }
930         else
931         if (!isUnreservedCharacter(testChar) &&
932             USERINFO_CHARACTERS.indexOf(testChar) == -1) {
933           throw new MalformedURIException(
934               MessageFormat.format(Messages.getString("URI.userinfoContainsInvalidCharacter"), new Object JavaDoc[] { String.valueOf(testChar) } ) ); //$NON-NLS-1$
935
}
936
937         index++;
938       }
939     }
940
941     m_userinfo = p_userinfo;
942   }
943
944   /**
945    * Set the host for this URI. If null is passed in, the userinfo field is also set to null and the port is set to -1.
946    *
947    * @param p_host the host for this URI
948    *
949    * @throws MalformedURIException if p_host is not a valid IP address or DNS hostname.
950    */

951   public void setHost(String JavaDoc p_host) throws MalformedURIException {
952
953     if (p_host == null || p_host.trim().length() == 0) {
954       m_host = p_host;
955       m_userinfo = null;
956       m_port = -1;
957     }
958     else
959     if (!isWellFormedAddress(p_host)) {
960       throw new MalformedURIException(Messages.getString("URI.hostIsNotWellFormed")); //$NON-NLS-1$
961
}
962
963     m_host = p_host;
964   }
965
966   /**
967    * Set the port for this URI. -1 is used to indicate that the port is not specified, otherwise valid port numbers are between 0
968    * and 65535. If a valid port number is passed in and the host field is null, an exception is thrown.
969    *
970    * @param p_port the port number for this URI
971    *
972        * @throws MalformedURIException if p_port is not -1 and not a valid port number
973    */

974   public void setPort(int p_port) throws MalformedURIException {
975
976     if (p_port >= 0 && p_port <= 65535) {
977       if (m_host == null) {
978         throw new MalformedURIException(Messages.getString("URI.portCannotBeSetWhenHostIsNull")); //$NON-NLS-1$
979
}
980     }
981     else
982     if (p_port != -1) {
983       throw new MalformedURIException(Messages.getString("URI.invalidPortNumber")); //$NON-NLS-1$
984
}
985
986     m_port = p_port;
987   }
988
989   /**
990    * Set the path for this URI. If the supplied path is null, then the query string and fragment are set to null as well. If the
991    * supplied path includes a query string and/or fragment, these fields will be parsed and set as well. Note that, for URIs
992    * following the "generic URI" syntax, the path specified should start with a slash. For URIs that do not follow the generic
993    * URI syntax, this method sets the scheme-specific part.
994    *
995    * @param p_path the path for this URI (may be null)
996    *
997    * @throws MalformedURIException if p_path contains invalid characters
998    */

999   public void setPath(String JavaDoc p_path) throws MalformedURIException {
1000
1001    if (p_path == null) {
1002      m_path = null;
1003      m_queryString = null;
1004      m_fragment = null;
1005    }
1006    else {
1007      initializePath(p_path);
1008    }
1009  }
1010
1011  /**
1012   * Append to the end of the path of this URI. If the current path does not end in a slash and the path to be appended does not
1013   * begin with a slash, a slash will be appended to the current path before the new segment is added. Also, if the current path
1014   * ends in a slash and the new segment begins with a slash, the extra slash will be removed before the new segment is appended.
1015   *
1016   * @param p_addToPath the new segment to be added to the current path
1017   *
1018   * @throws MalformedURIException if p_addToPath contains syntax errors
1019   */

1020  public void appendPath(String JavaDoc p_addToPath) throws MalformedURIException {
1021
1022    if (p_addToPath == null || p_addToPath.trim().length() == 0) {
1023      return;
1024    }
1025
1026    if (!isURIString(p_addToPath)) {
1027      throw new MalformedURIException(Messages.getString("URI.pathContainsInvalidCharacterShort")); //$NON-NLS-1$
1028
}
1029
1030    if (m_path == null || m_path.trim().length() == 0) {
1031      if (p_addToPath.startsWith("/")) { //$NON-NLS-1$
1032
m_path = p_addToPath;
1033      }
1034      else {
1035        m_path = "/" + p_addToPath; //$NON-NLS-1$
1036
}
1037    }
1038    else
1039    if (m_path.endsWith("/")) { //$NON-NLS-1$
1040
if (p_addToPath.startsWith("/")) { //$NON-NLS-1$
1041
m_path = m_path.concat(p_addToPath.substring(1));
1042      }
1043      else {
1044        m_path = m_path.concat(p_addToPath);
1045      }
1046    }
1047    else {
1048      if (p_addToPath.startsWith("/")) { //$NON-NLS-1$
1049
m_path = m_path.concat(p_addToPath);
1050      }
1051      else {
1052        m_path = m_path.concat("/" + p_addToPath); //$NON-NLS-1$
1053
}
1054    }
1055  }
1056
1057  /**
1058   * Set the query string for this URI. A non-null value is valid only if this is an URI conforming to the generic URI syntax and
1059   * the path value is not null.
1060   *
1061   * @param p_queryString the query string for this URI
1062   *
1063   * @throws MalformedURIException if p_queryString is not null and this URI does not conform to the generic URI syntax or if the
1064   * path is null
1065   */

1066  public void setQueryString(String JavaDoc p_queryString) throws MalformedURIException {
1067
1068    if (p_queryString == null) {
1069      m_queryString = null;
1070    }
1071    else
1072    if (!isGenericURI()) {
1073      throw new MalformedURIException(
1074          Messages.getString("URI.queryStringCanOnlyBeSetForGenericURI")); //$NON-NLS-1$
1075
}
1076    else
1077    if (getPath() == null) {
1078      throw new MalformedURIException(
1079          Messages.getString("URI.queryStringCannotBeSetWhenPathIsNull")); //$NON-NLS-1$
1080
}
1081    else
1082    if (!isURIString(p_queryString)) {
1083      throw new MalformedURIException(
1084          Messages.getString("URI.queryStringContainsInvalidCharacter")); //$NON-NLS-1$
1085
}
1086    else {
1087      m_queryString = p_queryString;
1088    }
1089  }
1090
1091  /**
1092   * Set the fragment for this URI. A non-null value is valid only if this is a URI conforming to the generic URI syntax and the
1093   * path value is not null.
1094   *
1095   * @param p_fragment the fragment for this URI
1096   *
1097   * @throws MalformedURIException if p_fragment is not null and this URI does not conform to the generic URI syntax or if the
1098   * path is null
1099   */

1100  public void setFragment(String JavaDoc p_fragment) throws MalformedURIException {
1101
1102    if (p_fragment == null) {
1103      m_fragment = null;
1104    }
1105    else
1106    if (!isGenericURI()) {
1107      throw new MalformedURIException(
1108          Messages.getString("URI.fragmentCanOnlyBeSetForAGenericURI")); //$NON-NLS-1$
1109
}
1110    else
1111    if (getPath() == null) {
1112      throw new MalformedURIException(
1113          Messages.getString("URI.fragmentCannotBeSetWhenPathIsNull")); //$NON-NLS-1$
1114
}
1115    else
1116    if (!isURIString(p_fragment)) {
1117      throw new MalformedURIException(Messages.getString("URI.fragmentContainsInvalidCharacter")); //$NON-NLS-1$
1118
}
1119    else {
1120      m_fragment = p_fragment;
1121    }
1122  }
1123
1124  /**
1125   * Determines if the passed-in Object is equivalent to this URI.
1126   *
1127   * @param p_test the Object to test for equality.
1128   *
1129   * @return true if p_test is a URI with all values equal to this URI, false otherwise
1130   */

1131  public boolean equals(Object JavaDoc p_test) {
1132
1133    if (p_test instanceof URI) {
1134      URI testURI = (URI) p_test;
1135
1136      if ( ( (m_scheme == null && testURI.m_scheme == null)
1137            ||
1138            (m_scheme != null && testURI.m_scheme != null &&
1139             m_scheme.equals(testURI.m_scheme)))
1140          && ( (m_userinfo == null && testURI.m_userinfo == null)
1141              ||
1142              (m_userinfo != null && testURI.m_userinfo != null &&
1143               m_userinfo.equals(testURI.m_userinfo)))
1144          && ( (m_host == null && testURI.m_host == null)
1145              ||
1146              (m_host != null && testURI.m_host != null &&
1147               m_host.equals(testURI.m_host)))
1148          && m_port == testURI.m_port
1149          && ( (m_path == null && testURI.m_path == null)
1150              ||
1151              (m_path != null && testURI.m_path != null &&
1152               m_path.equals(testURI.m_path)))
1153          && ( (m_queryString == null && testURI.m_queryString == null)
1154              ||
1155              (m_queryString != null && testURI.m_queryString != null &&
1156               m_queryString.equals(testURI.m_queryString)))
1157          && ( (m_fragment == null && testURI.m_fragment == null)
1158              ||
1159              (m_fragment != null && testURI.m_fragment != null &&
1160               m_fragment.equals(testURI.m_fragment)))) {
1161        return true;
1162      }
1163    }
1164
1165    return false;
1166  }
1167
1168  /**
1169   * Get the URI as a string specification. See RFC 2396 Section 5.2.
1170   *
1171   * @return the URI string specification
1172   */

1173  public String JavaDoc toString() {
1174
1175    StringBuffer JavaDoc uriSpecString = new StringBuffer JavaDoc();
1176
1177    if (m_scheme != null) {
1178      uriSpecString.append(m_scheme);
1179      uriSpecString.append(':');
1180    }
1181
1182    uriSpecString.append(getSchemeSpecificPart());
1183
1184    return uriSpecString.toString();
1185  }
1186
1187  /**
1188   * Get the indicator as to whether this URI uses the "generic URI" syntax.
1189   *
1190   * @return true if this URI uses the "generic URI" syntax, false otherwise
1191   */

1192  public boolean isGenericURI() {
1193
1194    // presence of the host (whether valid or empty) means
1195
// double-slashes which means generic uri
1196
return (m_host != null);
1197  }
1198
1199  /**
1200   * Determine whether a scheme conforms to the rules for a scheme name. A scheme is conformant if it starts with an
1201   * alphanumeric, and contains only alphanumerics, '+','-' and '.'.
1202   *
1203   * @param p_scheme The sheme name to check
1204   * @return true if the scheme is conformant, false otherwise
1205   */

1206  public static boolean isConformantSchemeName(String JavaDoc p_scheme) {
1207
1208    if (p_scheme == null || p_scheme.trim().length() == 0) {
1209      return false;
1210    }
1211
1212    if (!isAlpha(p_scheme.charAt(0))) {
1213      return false;
1214    }
1215
1216    char testChar;
1217
1218    for (int i = 1; i < p_scheme.length(); i++) {
1219      testChar = p_scheme.charAt(i);
1220
1221      if (!isAlphanum(testChar) && SCHEME_CHARACTERS.indexOf(testChar) == -1) {
1222        return false;
1223      }
1224    }
1225
1226    return true;
1227  }
1228
1229  /**
1230   * Determine whether a string is syntactically capable of representing a valid IPv4 address or the domain name of a network
1231   * host. A valid IPv4 address consists of four decimal digit groups separated by a '.'. A hostname consists of domain labels
1232   * (each of which must begin and end with an alphanumeric but may contain '-') separated & by a '.'. See RFC 2396 Section
1233   * 3.2.2.
1234   *
1235   * @param p_address The address string to check
1236       * @return true if the string is a syntactically valid IPv4 address or hostname
1237   */

1238  public static boolean isWellFormedAddress(String JavaDoc p_address) {
1239
1240    if (p_address == null) {
1241      return false;
1242    }
1243
1244    String JavaDoc address = p_address.trim();
1245    int addrLength = address.length();
1246
1247    if (addrLength == 0 || addrLength > 255) {
1248      return false;
1249    }
1250
1251    if (address.startsWith(".") || address.startsWith("-")) { //$NON-NLS-1$ //$NON-NLS-2$
1252
return false;
1253    }
1254
1255    // rightmost domain label starting with digit indicates IP address
1256
// since top level domain label can only start with an alpha
1257
// see RFC 2396 Section 3.2.2
1258
int index = address.lastIndexOf('.');
1259
1260    if (address.endsWith(".")) { //$NON-NLS-1$
1261
index = address.substring(0, index).lastIndexOf('.');
1262    }
1263
1264    if (index + 1 < addrLength && isDigit(p_address.charAt(index + 1))) {
1265      char testChar;
1266      int numDots = 0;
1267
1268      // make sure that 1) we see only digits and dot separators, 2) that
1269
// any dot separator is preceded and followed by a digit and
1270
// 3) that we find 3 dots
1271
for (int i = 0; i < addrLength; i++) {
1272        testChar = address.charAt(i);
1273
1274        if (testChar == '.') {
1275          if (!isDigit(address.charAt(i - 1)) ||
1276              (i + 1 < addrLength && !isDigit(address.charAt(i + 1)))) {
1277            return false;
1278          }
1279
1280          numDots++;
1281        }
1282        else
1283        if (!isDigit(testChar)) {
1284          return false;
1285        }
1286      }
1287
1288      if (numDots != 3) {
1289        return false;
1290      }
1291    }
1292    else {
1293
1294      // domain labels can contain alphanumerics and '-"
1295
// but must start and end with an alphanumeric
1296
char testChar;
1297
1298      for (int i = 0; i < addrLength; i++) {
1299        testChar = address.charAt(i);
1300
1301        if (testChar == '.') {
1302          if (!isAlphanum(address.charAt(i - 1))) {
1303            return false;
1304          }
1305
1306          if (i + 1 < addrLength && !isAlphanum(address.charAt(i + 1))) {
1307            return false;
1308          }
1309        }
1310        else
1311        if (!isAlphanum(testChar) && testChar != '-') {
1312          return false;
1313        }
1314      }
1315    }
1316
1317    return true;
1318  }
1319
1320  /**
1321   * Determine whether a char is a digit.
1322   *
1323   * @param p_char the character to check
1324   * @return true if the char is betweeen '0' and '9', false otherwise
1325   */

1326  private static boolean isDigit(char p_char) {
1327    return p_char >= '0' && p_char <= '9';
1328  }
1329
1330  /**
1331   * Determine whether a character is a hexadecimal character.
1332   *
1333   * @param p_char the character to check
1334   * @return true if the char is betweeen '0' and '9', 'a' and 'f' or 'A' and 'F', false otherwise
1335   */

1336  private static boolean isHex(char p_char) {
1337    return (isDigit(p_char) || (p_char >= 'a' && p_char <= 'f') ||
1338            (p_char >= 'A' && p_char <= 'F'));
1339  }
1340
1341  /**
1342   * Determine whether a char is an alphabetic character: a-z or A-Z
1343   *
1344   * @param p_char the character to check
1345   * @return true if the char is alphabetic, false otherwise
1346   */

1347  private static boolean isAlpha(char p_char) {
1348    return ( (p_char >= 'a' && p_char <= 'z') ||
1349            (p_char >= 'A' && p_char <= 'Z'));
1350  }
1351
1352  /**
1353   * Determine whether a char is an alphanumeric: 0-9, a-z or A-Z
1354   *
1355   * @param p_char the character to check
1356   * @return true if the char is alphanumeric, false otherwise
1357   */

1358  private static boolean isAlphanum(char p_char) {
1359    return (isAlpha(p_char) || isDigit(p_char));
1360  }
1361
1362  /**
1363   * Determine whether a character is a reserved character: ';', '/', '?', ':', '@', '&', '=', '+', '$' or ','
1364   *
1365   * @param p_char the character to check
1366   * @return true if the string contains any reserved characters
1367   */

1368  private static boolean isReservedCharacter(char p_char) {
1369    return RESERVED_CHARACTERS.indexOf(p_char) != -1;
1370  }
1371
1372  /**
1373   * Determine whether a char is an unreserved character.
1374   *
1375   * @param p_char the character to check
1376   * @return true if the char is unreserved, false otherwise
1377   */

1378  private static boolean isUnreservedCharacter(char p_char) {
1379    return (isAlphanum(p_char) || MARK_CHARACTERS.indexOf(p_char) != -1);
1380  }
1381
1382  /**
1383   * Determine whether a given string contains only URI characters (also called "uric" in RFC 2396). uric consist of all reserved
1384   * characters, unreserved characters and escaped characters.
1385   *
1386   * @param p_uric URI string
1387   * @return true if the string is comprised of uric, false otherwise
1388   */

1389  private static boolean isURIString(String JavaDoc p_uric) {
1390
1391    if (p_uric == null) {
1392      return false;
1393    }
1394
1395    int end = p_uric.length();
1396    char testChar = '\0';
1397
1398    for (int i = 0; i < end; i++) {
1399      testChar = p_uric.charAt(i);
1400
1401      if (testChar == '%') {
1402        if (i + 2 >= end || !isHex(p_uric.charAt(i + 1)) ||
1403            !isHex(p_uric.charAt(i + 2))) {
1404          return false;
1405        }
1406        else {
1407          i += 2;
1408
1409          continue;
1410        }
1411      }
1412
1413      if (isReservedCharacter(testChar) || isUnreservedCharacter(testChar)) {
1414        continue;
1415      }
1416      else {
1417        return false;
1418      }
1419    }
1420
1421    return true;
1422  }
1423}
1424
Popular Tags