1 /* 2 * reserved comment block 3 * DO NOT REMOVE OR ALTER! 4 */ 5 /* 6 * Copyright 1999-2004 The Apache Software Foundation. 7 * 8 * Licensed under the Apache License, Version 2.0 (the "License"); 9 * you may not use this file except in compliance with the License. 10 * You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 */ 20 /* 21 * $Id: URI.java,v 1.2.4.1 2005/09/15 08:16:00 suresh_emailid Exp $ 22 */ 23 package com.sun.org.apache.xml.internal.utils; 24 25 import java.io.IOException; 26 import java.io.Serializable; 27 28 import com.sun.org.apache.xml.internal.res.XMLErrorResources; 29 import com.sun.org.apache.xml.internal.res.XMLMessages; 30 import java.util.Objects; 31 32 /** 33 * A class to represent a Uniform Resource Identifier (URI). This class 34 * is designed to handle the parsing of URIs and provide access to 35 * the various components (scheme, host, port, userinfo, path, query 36 * string and fragment) that may constitute a URI. 37 * <p> 38 * Parsing of a URI specification is done according to the URI 39 * syntax described in RFC 2396 40 * <http://www.ietf.org/rfc/rfc2396.txt?number=2396>. Every URI consists 41 * of a scheme, followed by a colon (':'), followed by a scheme-specific 42 * part. For URIs that follow the "generic URI" syntax, the scheme- 43 * specific part begins with two slashes ("//") and may be followed 44 * by an authority segment (comprised of user information, host, and 45 * port), path segment, query segment and fragment. Note that RFC 2396 46 * no longer specifies the use of the parameters segment and excludes 47 * the "user:password" syntax as part of the authority segment. If 48 * "user:password" appears in a URI, the entire user/password string 49 * is stored as userinfo. 50 * <p> 51 * For URIs that do not follow the "generic URI" syntax (e.g. mailto), 52 * the entire scheme-specific part is treated as the "path" portion 53 * of the URI. 54 * <p> 55 * Note that, unlike the java.net.URL class, this class does not provide 56 * any built-in network access functionality nor does it provide any 57 * scheme-specific functionality (for example, it does not know a 58 * default port for a specific scheme). Rather, it only knows the 59 * grammar and basic set of operations that can be applied to a URI. 60 * 61 * 62 */ 63 public class URI implements Serializable 64 { 65 static final long serialVersionUID = 7096266377907081897L; 66 67 /** 68 * MalformedURIExceptions are thrown in the process of building a URI 69 * or setting fields on a URI when an operation would result in an 70 * invalid URI specification. 71 * 72 */ 73 public static class MalformedURIException extends IOException 74 { 75 76 /** 77 * Constructs a <code>MalformedURIException</code> with no specified 78 * detail message. 79 */ 80 public MalformedURIException() 81 { 82 super(); 83 } 84 85 /** 86 * Constructs a <code>MalformedURIException</code> with the 87 * specified detail message. 88 * 89 * @param p_msg the detail message. 90 */ 91 public MalformedURIException(String p_msg) 92 { 93 super(p_msg); 94 } 95 } 96 97 /** reserved characters */ 98 private static final String RESERVED_CHARACTERS = ";/?:@&=+$,"; 99 100 /** 101 * URI punctuation mark characters - these, combined with 102 * alphanumerics, constitute the "unreserved" characters 103 */ 104 private static final String MARK_CHARACTERS = "-_.!~*'() "; 105 106 /** scheme can be composed of alphanumerics and these characters */ 107 private static final String SCHEME_CHARACTERS = "+-."; 108 109 /** 110 * userinfo can be composed of unreserved, escaped and these 111 * characters 112 */ 113 private static final String USERINFO_CHARACTERS = ";:&=+$,"; 114 115 /** Stores the scheme (usually the protocol) for this URI. 116 * @serial */ 117 private String m_scheme = null; 118 119 /** If specified, stores the userinfo for this URI; otherwise null. 120 * @serial */ 121 private String m_userinfo = null; 122 123 /** If specified, stores the host for this URI; otherwise null. 124 * @serial */ 125 private String m_host = null; 126 127 /** If specified, stores the port for this URI; otherwise -1. 128 * @serial */ 129 private int m_port = -1; 130 131 /** If specified, stores the path for this URI; otherwise null. 132 * @serial */ 133 private String m_path = null; 134 135 /** 136 * If specified, stores the query string for this URI; otherwise 137 * null. 138 * @serial 139 */ 140 private String m_queryString = null; 141 142 /** If specified, stores the fragment for this URI; otherwise null. 143 * @serial */ 144 private String m_fragment = null; 145 146 /** Indicate whether in DEBUG mode */ 147 private static boolean DEBUG = false; 148 149 /** 150 * Construct a new and uninitialized URI. 151 */ 152 public URI(){} 153 154 /** 155 * Construct a new URI from another URI. All fields for this URI are 156 * set equal to the fields of the URI passed in. 157 * 158 * @param p_other the URI to copy (cannot be null) 159 */ 160 public URI(URI p_other) 161 { 162 initialize(p_other); 163 } 164 165 /** 166 * Construct a new URI from a URI specification string. If the 167 * specification follows the "generic URI" syntax, (two slashes 168 * following the first colon), the specification will be parsed 169 * accordingly - setting the scheme, userinfo, host,port, path, query 170 * string and fragment fields as necessary. If the specification does 171 * not follow the "generic URI" syntax, the specification is parsed 172 * into a scheme and scheme-specific part (stored as the path) only. 173 * 174 * @param p_uriSpec the URI specification string (cannot be null or 175 * empty) 176 * 177 * @throws MalformedURIException if p_uriSpec violates any syntax 178 * rules 179 */ 180 public URI(String p_uriSpec) throws MalformedURIException 181 { 182 this((URI) null, p_uriSpec); 183 } 184 185 /** 186 * Construct a new URI from a base URI and a URI specification string. 187 * The URI specification string may be a relative URI. 188 * 189 * @param p_base the base URI (cannot be null if p_uriSpec is null or 190 * empty) 191 * @param p_uriSpec the URI specification string (cannot be null or 192 * empty if p_base is null) 193 * 194 * @throws MalformedURIException if p_uriSpec violates any syntax 195 * rules 196 */ 197 public URI(URI p_base, String p_uriSpec) throws MalformedURIException 198 { 199 initialize(p_base, p_uriSpec); 200 } 201 202 /** 203 * Construct a new URI that does not follow the generic URI syntax. 204 * Only the scheme and scheme-specific part (stored as the path) are 205 * initialized. 206 * 207 * @param p_scheme the URI scheme (cannot be null or empty) 208 * @param p_schemeSpecificPart the scheme-specific part (cannot be 209 * null or empty) 210 * 211 * @throws MalformedURIException if p_scheme violates any 212 * syntax rules 213 */ 214 public URI(String p_scheme, String p_schemeSpecificPart) 215 throws MalformedURIException 216 { 217 218 if (p_scheme == null || p_scheme.trim().length() == 0) 219 { 220 throw new MalformedURIException( 221 "Cannot construct URI with null/empty scheme!"); 222 } 223 224 if (p_schemeSpecificPart == null 225 || p_schemeSpecificPart.trim().length() == 0) 226 { 227 throw new MalformedURIException( 228 "Cannot construct URI with null/empty scheme-specific part!"); 229 } 230 231 setScheme(p_scheme); 232 setPath(p_schemeSpecificPart); 233 } 234 235 /** 236 * Construct a new URI that follows the generic URI syntax from its 237 * component parts. Each component is validated for syntax and some 238 * basic semantic checks are performed as well. See the individual 239 * setter methods for specifics. 240 * 241 * @param p_scheme the URI scheme (cannot be null or empty) 242 * @param p_host the hostname or IPv4 address for the URI 243 * @param p_path the URI path - if the path contains '?' or '#', 244 * then the query string and/or fragment will be 245 * set from the path; however, if the query and 246 * fragment are specified both in the path and as 247 * separate parameters, an exception is thrown 248 * @param p_queryString the URI query string (cannot be specified 249 * if path is null) 250 * @param p_fragment the URI fragment (cannot be specified if path 251 * is null) 252 * 253 * @throws MalformedURIException if any of the parameters violates 254 * syntax rules or semantic rules 255 */ 256 public URI(String p_scheme, String p_host, String p_path, String p_queryString, String p_fragment) 257 throws MalformedURIException 258 { 259 this(p_scheme, null, p_host, -1, p_path, p_queryString, p_fragment); 260 } 261 262 /** 263 * Construct a new URI that follows the generic URI syntax from its 264 * component parts. Each component is validated for syntax and some 265 * basic semantic checks are performed as well. See the individual 266 * setter methods for specifics. 267 * 268 * @param p_scheme the URI scheme (cannot be null or empty) 269 * @param p_userinfo the URI userinfo (cannot be specified if host 270 * is null) 271 * @param p_host the hostname or IPv4 address for the URI 272 * @param p_port the URI port (may be -1 for "unspecified"; cannot 273 * be specified if host is null) 274 * @param p_path the URI path - if the path contains '?' or '#', 275 * then the query string and/or fragment will be 276 * set from the path; however, if the query and 277 * fragment are specified both in the path and as 278 * separate parameters, an exception is thrown 279 * @param p_queryString the URI query string (cannot be specified 280 * if path is null) 281 * @param p_fragment the URI fragment (cannot be specified if path 282 * is null) 283 * 284 * @throws MalformedURIException if any of the parameters violates 285 * syntax rules or semantic rules 286 */ 287 public URI(String p_scheme, String p_userinfo, String p_host, int p_port, String p_path, String p_queryString, String p_fragment) 288 throws MalformedURIException 289 { 290 291 if (p_scheme == null || p_scheme.trim().length() == 0) 292 { 293 throw new MalformedURIException(XMLMessages.createXMLMessage(XMLErrorResources.ER_SCHEME_REQUIRED, null)); //"Scheme is required!"); 294 } 295 296 if (p_host == null) 297 { 298 if (p_userinfo != null) 299 { 300 throw new MalformedURIException( 301 XMLMessages.createXMLMessage(XMLErrorResources.ER_NO_USERINFO_IF_NO_HOST, null)); //"Userinfo may not be specified if host is not specified!"); 302 } 303 304 if (p_port != -1) 305 { 306 throw new MalformedURIException( 307 XMLMessages.createXMLMessage(XMLErrorResources.ER_NO_PORT_IF_NO_HOST, null)); //"Port may not be specified if host is not specified!"); 308 } 309 } 310 311 if (p_path != null) 312 { 313 if (p_path.indexOf('?') != -1 && p_queryString != null) 314 { 315 throw new MalformedURIException( 316 XMLMessages.createXMLMessage(XMLErrorResources.ER_NO_QUERY_STRING_IN_PATH, null)); //"Query string cannot be specified in path and query string!"); 317 } 318 319 if (p_path.indexOf('#') != -1 && p_fragment != null) 320 { 321 throw new MalformedURIException( 322 XMLMessages.createXMLMessage(XMLErrorResources.ER_NO_FRAGMENT_STRING_IN_PATH, null)); //"Fragment cannot be specified in both the path and fragment!"); 323 } 324 } 325 326 setScheme(p_scheme); 327 setHost(p_host); 328 setPort(p_port); 329 setUserinfo(p_userinfo); 330 setPath(p_path); 331 setQueryString(p_queryString); 332 setFragment(p_fragment); 333 } 334 335 /** 336 * Initialize all fields of this URI from another URI. 337 * 338 * @param p_other the URI to copy (cannot be null) 339 */ 340 private void initialize(URI p_other) 341 { 342 343 m_scheme = p_other.getScheme(); 344 m_userinfo = p_other.getUserinfo(); 345 m_host = p_other.getHost(); 346 m_port = p_other.getPort(); 347 m_path = p_other.getPath(); 348 m_queryString = p_other.getQueryString(); 349 m_fragment = p_other.getFragment(); 350 } 351 352 /** 353 * Initializes this URI from a base URI and a URI specification string. 354 * See RFC 2396 Section 4 and Appendix B for specifications on parsing 355 * the URI and Section 5 for specifications on resolving relative URIs 356 * and relative paths. 357 * 358 * @param p_base the base URI (may be null if p_uriSpec is an absolute 359 * URI) 360 * @param p_uriSpec the URI spec string which may be an absolute or 361 * relative URI (can only be null/empty if p_base 362 * is not null) 363 * 364 * @throws MalformedURIException if p_base is null and p_uriSpec 365 * is not an absolute URI or if 366 * p_uriSpec violates syntax rules 367 */ 368 private void initialize(URI p_base, String p_uriSpec) 369 throws MalformedURIException 370 { 371 372 if (p_base == null 373 && (p_uriSpec == null || p_uriSpec.trim().length() == 0)) 374 { 375 throw new MalformedURIException( 376 XMLMessages.createXMLMessage(XMLErrorResources.ER_CANNOT_INIT_URI_EMPTY_PARMS, null)); //"Cannot initialize URI with empty parameters."); 377 } 378 379 // just make a copy of the base if spec is empty 380 if (p_uriSpec == null || p_uriSpec.trim().length() == 0) 381 { 382 initialize(p_base); 383 384 return; 385 } 386 387 String uriSpec = p_uriSpec.trim(); 388 int uriSpecLen = uriSpec.length(); 389 int index = 0; 390 391 // check for scheme 392 int colonIndex = uriSpec.indexOf(':'); 393 if (colonIndex < 0) 394 { 395 if (p_base == null) 396 { 397 throw new MalformedURIException(XMLMessages.createXMLMessage(XMLErrorResources.ER_NO_SCHEME_IN_URI, new Object[]{uriSpec})); //"No scheme found in URI: "+uriSpec); 398 } 399 } 400 else 401 { 402 initializeScheme(uriSpec); 403 uriSpec = uriSpec.substring(colonIndex+1); 404 // This is a fix for XALANJ-2059. 405 if(m_scheme != null && p_base != null) 406 { 407 // a) If <uriSpec> starts with a slash (/), it means <uriSpec> is absolute 408 // and p_base can be ignored. 409 // For example, 410 // uriSpec = file:/myDIR/myXSLFile.xsl 411 // p_base = file:/myWork/ 412 // 413 // Here, uriSpec has absolute path after scheme file and : 414 // Hence p_base can be ignored. 415 // 416 // b) Similarily, according to RFC 2396, uri is resolved for <uriSpec> relative to <p_base> 417 // if scheme in <uriSpec> is same as scheme in <p_base>, else p_base can be ignored. 418 // 419 // c) if <p_base> is not hierarchical, it can be ignored. 420 // 421 if(uriSpec.startsWith("/") || !m_scheme.equals(p_base.m_scheme) || !p_base.getSchemeSpecificPart().startsWith("/")) 422 { 423 p_base = null; 424 } 425 } 426 // Fix for XALANJ-2059 427 uriSpecLen = uriSpec.length(); 428 } 429 430 // two slashes means generic URI syntax, so we get the authority 431 if (((index + 1) < uriSpecLen) 432 && (uriSpec.substring(index).startsWith("//"))) 433 { 434 index += 2; 435 436 int startPos = index; 437 438 // get authority - everything up to path, query or fragment 439 char testChar = '\0'; 440 441 while (index < uriSpecLen) 442 { 443 testChar = uriSpec.charAt(index); 444 445 if (testChar == '/' || testChar == '?' || testChar == '#') 446 { 447 break; 448 } 449 450 index++; 451 } 452 453 // if we found authority, parse it out, otherwise we set the 454 // host to empty string 455 if (index > startPos) 456 { 457 initializeAuthority(uriSpec.substring(startPos, index)); 458 } 459 else 460 { 461 m_host = ""; 462 } 463 } 464 465 initializePath(uriSpec.substring(index)); 466 467 // Resolve relative URI to base URI - see RFC 2396 Section 5.2 468 // In some cases, it might make more sense to throw an exception 469 // (when scheme is specified is the string spec and the base URI 470 // is also specified, for example), but we're just following the 471 // RFC specifications 472 if (p_base != null) 473 { 474 475 // check to see if this is the current doc - RFC 2396 5.2 #2 476 // note that this is slightly different from the RFC spec in that 477 // we don't include the check for query string being null 478 // - this handles cases where the urispec is just a query 479 // string or a fragment (e.g. "?y" or "#s") - 480 // see <http://www.ics.uci.edu/~fielding/url/test1.html> which 481 // identified this as a bug in the RFC 482 if (m_path.length() == 0 && m_scheme == null && m_host == null) 483 { 484 m_scheme = p_base.getScheme(); 485 m_userinfo = p_base.getUserinfo(); 486 m_host = p_base.getHost(); 487 m_port = p_base.getPort(); 488 m_path = p_base.getPath(); 489 490 if (m_queryString == null) 491 { 492 m_queryString = p_base.getQueryString(); 493 } 494 495 return; 496 } 497 498 // check for scheme - RFC 2396 5.2 #3 499 // if we found a scheme, it means absolute URI, so we're done 500 if (m_scheme == null) 501 { 502 m_scheme = p_base.getScheme(); 503 } 504 505 // check for authority - RFC 2396 5.2 #4 506 // if we found a host, then we've got a network path, so we're done 507 if (m_host == null) 508 { 509 m_userinfo = p_base.getUserinfo(); 510 m_host = p_base.getHost(); 511 m_port = p_base.getPort(); 512 } 513 else 514 { 515 return; 516 } 517 518 // check for absolute path - RFC 2396 5.2 #5 519 if (m_path.length() > 0 && m_path.startsWith("/")) 520 { 521 return; 522 } 523 524 // if we get to this point, we need to resolve relative path 525 // RFC 2396 5.2 #6 526 String path = ""; 527 String basePath = p_base.getPath(); 528 529 // 6a - get all but the last segment of the base URI path 530 if (basePath != null) 531 { 532 int lastSlash = basePath.lastIndexOf('/'); 533 534 if (lastSlash != -1) 535 { 536 path = basePath.substring(0, lastSlash + 1); 537 } 538 } 539 540 // 6b - append the relative URI path 541 path = path.concat(m_path); 542 543 // 6c - remove all "./" where "." is a complete path segment 544 index = -1; 545 546 while ((index = path.indexOf("/./")) != -1) 547 { 548 path = path.substring(0, index + 1).concat(path.substring(index + 3)); 549 } 550 551 // 6d - remove "." if path ends with "." as a complete path segment 552 if (path.endsWith("/.")) 553 { 554 path = path.substring(0, path.length() - 1); 555 } 556 557 // 6e - remove all "<segment>/../" where "<segment>" is a complete 558 // path segment not equal to ".." 559 index = -1; 560 561 int segIndex = -1; 562 String tempString = null; 563 564 while ((index = path.indexOf("/../")) > 0) 565 { 566 tempString = path.substring(0, path.indexOf("/../")); 567 segIndex = tempString.lastIndexOf('/'); 568 569 if (segIndex != -1) 570 { 571 if (!tempString.substring(segIndex++).equals("..")) 572 { 573 path = path.substring(0, segIndex).concat(path.substring(index 574 + 4)); 575 } 576 } 577 } 578 579 // 6f - remove ending "<segment>/.." where "<segment>" is a 580 // complete path segment 581 if (path.endsWith("/..")) 582 { 583 tempString = path.substring(0, path.length() - 3); 584 segIndex = tempString.lastIndexOf('/'); 585 586 if (segIndex != -1) 587 { 588 path = path.substring(0, segIndex + 1); 589 } 590 } 591 592 m_path = path; 593 } 594 } 595 596 /** 597 * Initialize the scheme for this URI from a URI string spec. 598 * 599 * @param p_uriSpec the URI specification (cannot be null) 600 * 601 * @throws MalformedURIException if URI does not have a conformant 602 * scheme 603 */ 604 private void initializeScheme(String p_uriSpec) throws MalformedURIException 605 { 606 607 int uriSpecLen = p_uriSpec.length(); 608 int index = 0; 609 String scheme = null; 610 char testChar = '\0'; 611 612 while (index < uriSpecLen) 613 { 614 testChar = p_uriSpec.charAt(index); 615 616 if (testChar == ':' || testChar == '/' || testChar == '?' 617 || testChar == '#') 618 { 619 break; 620 } 621 622 index++; 623 } 624 625 scheme = p_uriSpec.substring(0, index); 626 627 if (scheme.length() == 0) 628 { 629 throw new MalformedURIException(XMLMessages.createXMLMessage(XMLErrorResources.ER_NO_SCHEME_INURI, null)); //"No scheme found in URI."); 630 } 631 else 632 { 633 setScheme(scheme); 634 } 635 } 636 637 /** 638 * Initialize the authority (userinfo, host and port) for this 639 * URI from a URI string spec. 640 * 641 * @param p_uriSpec the URI specification (cannot be null) 642 * 643 * @throws MalformedURIException if p_uriSpec violates syntax rules 644 */ 645 private void initializeAuthority(String p_uriSpec) 646 throws MalformedURIException 647 { 648 649 int index = 0; 650 int start = 0; 651 int end = p_uriSpec.length(); 652 char testChar = '\0'; 653 String userinfo = null; 654 655 // userinfo is everything up @ 656 if (p_uriSpec.indexOf('@', start) != -1) 657 { 658 while (index < end) 659 { 660 testChar = p_uriSpec.charAt(index); 661 662 if (testChar == '@') 663 { 664 break; 665 } 666 667 index++; 668 } 669 670 userinfo = p_uriSpec.substring(start, index); 671 672 index++; 673 } 674 675 // host is everything up to ':' 676 String host = null; 677 678 start = index; 679 680 while (index < end) 681 { 682 testChar = p_uriSpec.charAt(index); 683 684 if (testChar == ':') 685 { 686 break; 687 } 688 689 index++; 690 } 691 692 host = p_uriSpec.substring(start, index); 693 694 int port = -1; 695 696 if (host.length() > 0) 697 { 698 699 // port 700 if (testChar == ':') 701 { 702 index++; 703 704 start = index; 705 706 while (index < end) 707 { 708 index++; 709 } 710 711 String portStr = p_uriSpec.substring(start, index); 712 713 if (portStr.length() > 0) 714 { 715 for (int i = 0; i < portStr.length(); i++) 716 { 717 if (!isDigit(portStr.charAt(i))) 718 { 719 throw new MalformedURIException( 720 portStr + " is invalid. Port should only contain digits!"); 721 } 722 } 723 724 try 725 { 726 port = Integer.parseInt(portStr); 727 } 728 catch (NumberFormatException nfe) 729 { 730 731 // can't happen 732 } 733 } 734 } 735 } 736 737 setHost(host); 738 setPort(port); 739 setUserinfo(userinfo); 740 } 741 742 /** 743 * Initialize the path for this URI from a URI string spec. 744 * 745 * @param p_uriSpec the URI specification (cannot be null) 746 * 747 * @throws MalformedURIException if p_uriSpec violates syntax rules 748 */ 749 private void initializePath(String p_uriSpec) throws MalformedURIException 750 { 751 752 if (p_uriSpec == null) 753 { 754 throw new MalformedURIException( 755 "Cannot initialize path from null string!"); 756 } 757 758 int index = 0; 759 int start = 0; 760 int end = p_uriSpec.length(); 761 char testChar = '\0'; 762 763 // path - everything up to query string or fragment 764 while (index < end) 765 { 766 testChar = p_uriSpec.charAt(index); 767 768 if (testChar == '?' || testChar == '#') 769 { 770 break; 771 } 772 773 // check for valid escape sequence 774 if (testChar == '%') 775 { 776 if (index + 2 >= end ||!isHex(p_uriSpec.charAt(index + 1)) 777 ||!isHex(p_uriSpec.charAt(index + 2))) 778 { 779 throw new MalformedURIException( 780 XMLMessages.createXMLMessage(XMLErrorResources.ER_PATH_CONTAINS_INVALID_ESCAPE_SEQUENCE, null)); //"Path contains invalid escape sequence!"); 781 } 782 } 783 else if (!isReservedCharacter(testChar) 784 &&!isUnreservedCharacter(testChar)) 785 { 786 if ('\\' != testChar) 787 throw new MalformedURIException(XMLMessages.createXMLMessage(XMLErrorResources.ER_PATH_INVALID_CHAR, new Object[]{String.valueOf(testChar)})); //"Path contains invalid character: " 788 //+ testChar); 789 } 790 791 index++; 792 } 793 794 m_path = p_uriSpec.substring(start, index); 795 796 // query - starts with ? and up to fragment or end 797 if (testChar == '?') 798 { 799 index++; 800 801 start = index; 802 803 while (index < end) 804 { 805 testChar = p_uriSpec.charAt(index); 806 807 if (testChar == '#') 808 { 809 break; 810 } 811 812 if (testChar == '%') 813 { 814 if (index + 2 >= end ||!isHex(p_uriSpec.charAt(index + 1)) 815 ||!isHex(p_uriSpec.charAt(index + 2))) 816 { 817 throw new MalformedURIException( 818 "Query string contains invalid escape sequence!"); 819 } 820 } 821 else if (!isReservedCharacter(testChar) 822 &&!isUnreservedCharacter(testChar)) 823 { 824 throw new MalformedURIException( 825 "Query string contains invalid character:" + testChar); 826 } 827 828 index++; 829 } 830 831 m_queryString = p_uriSpec.substring(start, index); 832 } 833 834 // fragment - starts with # 835 if (testChar == '#') 836 { 837 index++; 838 839 start = index; 840 841 while (index < end) 842 { 843 testChar = p_uriSpec.charAt(index); 844 845 if (testChar == '%') 846 { 847 if (index + 2 >= end ||!isHex(p_uriSpec.charAt(index + 1)) 848 ||!isHex(p_uriSpec.charAt(index + 2))) 849 { 850 throw new MalformedURIException( 851 "Fragment contains invalid escape sequence!"); 852 } 853 } 854 else if (!isReservedCharacter(testChar) 855 &&!isUnreservedCharacter(testChar)) 856 { 857 throw new MalformedURIException( 858 "Fragment contains invalid character:" + testChar); 859 } 860 861 index++; 862 } 863 864 m_fragment = p_uriSpec.substring(start, index); 865 } 866 } 867 868 /** 869 * Get the scheme for this URI. 870 * 871 * @return the scheme for this URI 872 */ 873 public String getScheme() 874 { 875 return m_scheme; 876 } 877 878 /** 879 * Get the scheme-specific part for this URI (everything following the 880 * scheme and the first colon). See RFC 2396 Section 5.2 for spec. 881 * 882 * @return the scheme-specific part for this URI 883 */ 884 public String getSchemeSpecificPart() 885 { 886 887 final StringBuilder schemespec = new StringBuilder(); 888 889 if (m_userinfo != null || m_host != null || m_port != -1) 890 { 891 schemespec.append("//"); 892 } 893 894 if (m_userinfo != null) 895 { 896 schemespec.append(m_userinfo); 897 schemespec.append('@'); 898 } 899 900 if (m_host != null) 901 { 902 schemespec.append(m_host); 903 } 904 905 if (m_port != -1) 906 { 907 schemespec.append(':'); 908 schemespec.append(m_port); 909 } 910 911 if (m_path != null) 912 { 913 schemespec.append((m_path)); 914 } 915 916 if (m_queryString != null) 917 { 918 schemespec.append('?'); 919 schemespec.append(m_queryString); 920 } 921 922 if (m_fragment != null) 923 { 924 schemespec.append('#'); 925 schemespec.append(m_fragment); 926 } 927 928 return schemespec.toString(); 929 } 930 931 /** 932 * Get the userinfo for this URI. 933 * 934 * @return the userinfo for this URI (null if not specified). 935 */ 936 public String getUserinfo() 937 { 938 return m_userinfo; 939 } 940 941 /** 942 * Get the host for this URI. 943 * 944 * @return the host for this URI (null if not specified). 945 */ 946 public String getHost() 947 { 948 return m_host; 949 } 950 951 /** 952 * Get the port for this URI. 953 * 954 * @return the port for this URI (-1 if not specified). 955 */ 956 public int getPort() 957 { 958 return m_port; 959 } 960 961 /** 962 * Get the path for this URI (optionally with the query string and 963 * fragment). 964 * 965 * @param p_includeQueryString if true (and query string is not null), 966 * then a "?" followed by the query string 967 * will be appended 968 * @param p_includeFragment if true (and fragment is not null), 969 * then a "#" followed by the fragment 970 * will be appended 971 * 972 * @return the path for this URI possibly including the query string 973 * and fragment 974 */ 975 public String getPath(boolean p_includeQueryString, 976 boolean p_includeFragment) 977 { 978 979 final StringBuilder pathString = new StringBuilder(m_path); 980 981 if (p_includeQueryString && m_queryString != null) 982 { 983 pathString.append('?'); 984 pathString.append(m_queryString); 985 } 986 987 if (p_includeFragment && m_fragment != null) 988 { 989 pathString.append('#'); 990 pathString.append(m_fragment); 991 } 992 993 return pathString.toString(); 994 } 995 996 /** 997 * Get the path for this URI. Note that the value returned is the path 998 * only and does not include the query string or fragment. 999 * 1000 * @return the path for this URI. 1001 */ 1002 public String getPath() 1003 { 1004 return m_path; 1005 } 1006 1007 /** 1008 * Get the query string for this URI. 1009 * 1010 * @return the query string for this URI. Null is returned if there 1011 * was no "?" in the URI spec, empty string if there was a 1012 * "?" but no query string following it. 1013 */ 1014 public String getQueryString() 1015 { 1016 return m_queryString; 1017 } 1018 1019 /** 1020 * Get the fragment for this URI. 1021 * 1022 * @return the fragment for this URI. Null is returned if there 1023 * was no "#" in the URI spec, empty string if there was a 1024 * "#" but no fragment following it. 1025 */ 1026 public String getFragment() 1027 { 1028 return m_fragment; 1029 } 1030 1031 /** 1032 * Set the scheme for this URI. The scheme is converted to lowercase 1033 * before it is set. 1034 * 1035 * @param p_scheme the scheme for this URI (cannot be null) 1036 * 1037 * @throws MalformedURIException if p_scheme is not a conformant 1038 * scheme name 1039 */ 1040 public void setScheme(String p_scheme) throws MalformedURIException 1041 { 1042 1043 if (p_scheme == null) 1044 { 1045 throw new MalformedURIException(XMLMessages.createXMLMessage(XMLErrorResources.ER_SCHEME_FROM_NULL_STRING, null)); //"Cannot set scheme from null string!"); 1046 } 1047 1048 if (!isConformantSchemeName(p_scheme)) 1049 { 1050 throw new MalformedURIException(XMLMessages.createXMLMessage(XMLErrorResources.ER_SCHEME_NOT_CONFORMANT, null)); //"The scheme is not conformant."); 1051 } 1052 1053 m_scheme = p_scheme.toLowerCase(); 1054 } 1055 1056 /** 1057 * Set the userinfo for this URI. If a non-null value is passed in and 1058 * the host value is null, then an exception is thrown. 1059 * 1060 * @param p_userinfo the userinfo for this URI 1061 * 1062 * @throws MalformedURIException if p_userinfo contains invalid 1063 * characters 1064 */ 1065 public void setUserinfo(String p_userinfo) throws MalformedURIException 1066 { 1067 1068 if (p_userinfo == null) 1069 { 1070 m_userinfo = null; 1071 } 1072 else 1073 { 1074 if (m_host == null) 1075 { 1076 throw new MalformedURIException( 1077 "Userinfo cannot be set when host is null!"); 1078 } 1079 1080 // userinfo can contain alphanumerics, mark characters, escaped 1081 // and ';',':','&','=','+','$',',' 1082 int index = 0; 1083 int end = p_userinfo.length(); 1084 char testChar = '\0'; 1085 1086 while (index < end) 1087 { 1088 testChar = p_userinfo.charAt(index); 1089 1090 if (testChar == '%') 1091 { 1092 if (index + 2 >= end ||!isHex(p_userinfo.charAt(index + 1)) 1093 ||!isHex(p_userinfo.charAt(index + 2))) 1094 { 1095 throw new MalformedURIException( 1096 "Userinfo contains invalid escape sequence!"); 1097 } 1098 } 1099 else if (!isUnreservedCharacter(testChar) 1100 && USERINFO_CHARACTERS.indexOf(testChar) == -1) 1101 { 1102 throw new MalformedURIException( 1103 "Userinfo contains invalid character:" + testChar); 1104 } 1105 1106 index++; 1107 } 1108 } 1109 1110 m_userinfo = p_userinfo; 1111 } 1112 1113 /** 1114 * Set the host for this URI. If null is passed in, the userinfo 1115 * field is also set to null and the port is set to -1. 1116 * 1117 * @param p_host the host for this URI 1118 * 1119 * @throws MalformedURIException if p_host is not a valid IP 1120 * address or DNS hostname. 1121 */ 1122 public void setHost(String p_host) throws MalformedURIException 1123 { 1124 1125 if (p_host == null || p_host.trim().length() == 0) 1126 { 1127 m_host = p_host; 1128 m_userinfo = null; 1129 m_port = -1; 1130 } 1131 else if (!isWellFormedAddress(p_host)) 1132 { 1133 throw new MalformedURIException(XMLMessages.createXMLMessage(XMLErrorResources.ER_HOST_ADDRESS_NOT_WELLFORMED, null)); //"Host is not a well formed address!"); 1134 } 1135 1136 m_host = p_host; 1137 } 1138 1139 /** 1140 * Set the port for this URI. -1 is used to indicate that the port is 1141 * not specified, otherwise valid port numbers are between 0 and 65535. 1142 * If a valid port number is passed in and the host field is null, 1143 * an exception is thrown. 1144 * 1145 * @param p_port the port number for this URI 1146 * 1147 * @throws MalformedURIException if p_port is not -1 and not a 1148 * valid port number 1149 */ 1150 public void setPort(int p_port) throws MalformedURIException 1151 { 1152 1153 if (p_port >= 0 && p_port <= 65535) 1154 { 1155 if (m_host == null) 1156 { 1157 throw new MalformedURIException( 1158 XMLMessages.createXMLMessage(XMLErrorResources.ER_PORT_WHEN_HOST_NULL, null)); //"Port cannot be set when host is null!"); 1159 } 1160 } 1161 else if (p_port != -1) 1162 { 1163 throw new MalformedURIException(XMLMessages.createXMLMessage(XMLErrorResources.ER_INVALID_PORT, null)); //"Invalid port number!"); 1164 } 1165 1166 m_port = p_port; 1167 } 1168 1169 /** 1170 * Set the path for this URI. If the supplied path is null, then the 1171 * query string and fragment are set to null as well. If the supplied 1172 * path includes a query string and/or fragment, these fields will be 1173 * parsed and set as well. Note that, for URIs following the "generic 1174 * URI" syntax, the path specified should start with a slash. 1175 * For URIs that do not follow the generic URI syntax, this method 1176 * sets the scheme-specific part. 1177 * 1178 * @param p_path the path for this URI (may be null) 1179 * 1180 * @throws MalformedURIException if p_path contains invalid 1181 * characters 1182 */ 1183 public void setPath(String p_path) throws MalformedURIException 1184 { 1185 1186 if (p_path == null) 1187 { 1188 m_path = null; 1189 m_queryString = null; 1190 m_fragment = null; 1191 } 1192 else 1193 { 1194 initializePath(p_path); 1195 } 1196 } 1197 1198 /** 1199 * Append to the end of the path of this URI. If the current path does 1200 * not end in a slash and the path to be appended does not begin with 1201 * a slash, a slash will be appended to the current path before the 1202 * new segment is added. Also, if the current path ends in a slash 1203 * and the new segment begins with a slash, the extra slash will be 1204 * removed before the new segment is appended. 1205 * 1206 * @param p_addToPath the new segment to be added to the current path 1207 * 1208 * @throws MalformedURIException if p_addToPath contains syntax 1209 * errors 1210 */ 1211 public void appendPath(String p_addToPath) throws MalformedURIException 1212 { 1213 1214 if (p_addToPath == null || p_addToPath.trim().length() == 0) 1215 { 1216 return; 1217 } 1218 1219 if (!isURIString(p_addToPath)) 1220 { 1221 throw new MalformedURIException(XMLMessages.createXMLMessage(XMLErrorResources.ER_PATH_INVALID_CHAR, new Object[]{p_addToPath})); //"Path contains invalid character!"); 1222 } 1223 1224 if (m_path == null || m_path.trim().length() == 0) 1225 { 1226 if (p_addToPath.startsWith("/")) 1227 { 1228 m_path = p_addToPath; 1229 } 1230 else 1231 { 1232 m_path = "/" + p_addToPath; 1233 } 1234 } 1235 else if (m_path.endsWith("/")) 1236 { 1237 if (p_addToPath.startsWith("/")) 1238 { 1239 m_path = m_path.concat(p_addToPath.substring(1)); 1240 } 1241 else 1242 { 1243 m_path = m_path.concat(p_addToPath); 1244 } 1245 } 1246 else 1247 { 1248 if (p_addToPath.startsWith("/")) 1249 { 1250 m_path = m_path.concat(p_addToPath); 1251 } 1252 else 1253 { 1254 m_path = m_path.concat("/" + p_addToPath); 1255 } 1256 } 1257 } 1258 1259 /** 1260 * Set the query string for this URI. A non-null value is valid only 1261 * if this is an URI conforming to the generic URI syntax and 1262 * the path value is not null. 1263 * 1264 * @param p_queryString the query string for this URI 1265 * 1266 * @throws MalformedURIException if p_queryString is not null and this 1267 * URI does not conform to the generic 1268 * URI syntax or if the path is null 1269 */ 1270 public void setQueryString(String p_queryString) 1271 throws MalformedURIException 1272 { 1273 1274 if (p_queryString == null) 1275 { 1276 m_queryString = null; 1277 } 1278 else if (!isGenericURI()) 1279 { 1280 throw new MalformedURIException( 1281 "Query string can only be set for a generic URI!"); 1282 } 1283 else if (getPath() == null) 1284 { 1285 throw new MalformedURIException( 1286 "Query string cannot be set when path is null!"); 1287 } 1288 else if (!isURIString(p_queryString)) 1289 { 1290 throw new MalformedURIException( 1291 "Query string contains invalid character!"); 1292 } 1293 else 1294 { 1295 m_queryString = p_queryString; 1296 } 1297 } 1298 1299 /** 1300 * Set the fragment for this URI. A non-null value is valid only 1301 * if this is a URI conforming to the generic URI syntax and 1302 * the path value is not null. 1303 * 1304 * @param p_fragment the fragment for this URI 1305 * 1306 * @throws MalformedURIException if p_fragment is not null and this 1307 * URI does not conform to the generic 1308 * URI syntax or if the path is null 1309 */ 1310 public void setFragment(String p_fragment) throws MalformedURIException 1311 { 1312 1313 if (p_fragment == null) 1314 { 1315 m_fragment = null; 1316 } 1317 else if (!isGenericURI()) 1318 { 1319 throw new MalformedURIException( 1320 XMLMessages.createXMLMessage(XMLErrorResources.ER_FRAG_FOR_GENERIC_URI, null)); //"Fragment can only be set for a generic URI!"); 1321 } 1322 else if (getPath() == null) 1323 { 1324 throw new MalformedURIException( 1325 XMLMessages.createXMLMessage(XMLErrorResources.ER_FRAG_WHEN_PATH_NULL, null)); //"Fragment cannot be set when path is null!"); 1326 } 1327 else if (!isURIString(p_fragment)) 1328 { 1329 throw new MalformedURIException(XMLMessages.createXMLMessage(XMLErrorResources.ER_FRAG_INVALID_CHAR, null)); //"Fragment contains invalid character!"); 1330 } 1331 else 1332 { 1333 m_fragment = p_fragment; 1334 } 1335 } 1336 1337 /** 1338 * Determines if the passed-in Object is equivalent to this URI. 1339 * 1340 * @param p_test the Object to test for equality. 1341 * 1342 * @return true if p_test is a URI with all values equal to this 1343 * URI, false otherwise 1344 */ 1345 @Override 1346 public boolean equals(Object p_test) 1347 { 1348 1349 if (p_test instanceof URI) 1350 { 1351 URI testURI = (URI) p_test; 1352 1353 if (((m_scheme == null && testURI.m_scheme == null) || (m_scheme != null && testURI.m_scheme != null && m_scheme.equals( 1354 testURI.m_scheme))) && ((m_userinfo == null && testURI.m_userinfo == null) || (m_userinfo != null && testURI.m_userinfo != null && m_userinfo.equals( 1355 testURI.m_userinfo))) && ((m_host == null && testURI.m_host == null) || (m_host != null && testURI.m_host != null && m_host.equals( 1356 testURI.m_host))) && m_port == testURI.m_port && ((m_path == null && testURI.m_path == null) || (m_path != null && testURI.m_path != null && m_path.equals( 1357 testURI.m_path))) && ((m_queryString == null && testURI.m_queryString == null) || (m_queryString != null && testURI.m_queryString != null && m_queryString.equals( 1358 testURI.m_queryString))) && ((m_fragment == null && testURI.m_fragment == null) || (m_fragment != null && testURI.m_fragment != null && m_fragment.equals( 1359 testURI.m_fragment)))) 1360 { 1361 return true; 1362 } 1363 } 1364 1365 return false; 1366 } 1367 1368 @Override 1369 public int hashCode() { 1370 int hash = 7; 1371 hash = 59 * hash + Objects.hashCode(this.m_scheme); 1372 hash = 59 * hash + Objects.hashCode(this.m_userinfo); 1373 hash = 59 * hash + Objects.hashCode(this.m_host); 1374 hash = 59 * hash + this.m_port; 1375 hash = 59 * hash + Objects.hashCode(this.m_path); 1376 hash = 59 * hash + Objects.hashCode(this.m_queryString); 1377 hash = 59 * hash + Objects.hashCode(this.m_fragment); 1378 return hash; 1379 } 1380 1381 /** 1382 * Get the URI as a string specification. See RFC 2396 Section 5.2. 1383 * 1384 * @return the URI string specification 1385 */ 1386 @Override 1387 public String toString() 1388 { 1389 1390 final StringBuilder uriSpecString = new StringBuilder(); 1391 1392 if (m_scheme != null) 1393 { 1394 uriSpecString.append(m_scheme); 1395 uriSpecString.append(':'); 1396 } 1397 1398 uriSpecString.append(getSchemeSpecificPart()); 1399 1400 return uriSpecString.toString(); 1401 } 1402 1403 /** 1404 * Get the indicator as to whether this URI uses the "generic URI" 1405 * syntax. 1406 * 1407 * @return true if this URI uses the "generic URI" syntax, false 1408 * otherwise 1409 */ 1410 public boolean isGenericURI() 1411 { 1412 1413 // presence of the host (whether valid or empty) means 1414 // double-slashes which means generic uri 1415 return (m_host != null); 1416 } 1417 1418 /** 1419 * Determine whether a scheme conforms to the rules for a scheme name. 1420 * A scheme is conformant if it starts with an alphanumeric, and 1421 * contains only alphanumerics, '+','-' and '.'. 1422 * 1423 * 1424 * @param p_scheme The sheme name to check 1425 * @return true if the scheme is conformant, false otherwise 1426 */ 1427 public static boolean isConformantSchemeName(String p_scheme) 1428 { 1429 1430 if (p_scheme == null || p_scheme.trim().length() == 0) 1431 { 1432 return false; 1433 } 1434 1435 if (!isAlpha(p_scheme.charAt(0))) 1436 { 1437 return false; 1438 } 1439 1440 char testChar; 1441 1442 for (int i = 1; i < p_scheme.length(); i++) 1443 { 1444 testChar = p_scheme.charAt(i); 1445 1446 if (!isAlphanum(testChar) && SCHEME_CHARACTERS.indexOf(testChar) == -1) 1447 { 1448 return false; 1449 } 1450 } 1451 1452 return true; 1453 } 1454 1455 /** 1456 * Determine whether a string is syntactically capable of representing 1457 * a valid IPv4 address or the domain name of a network host. A valid 1458 * IPv4 address consists of four decimal digit groups separated by a 1459 * '.'. A hostname consists of domain labels (each of which must 1460 * begin and end with an alphanumeric but may contain '-') separated 1461 * & by a '.'. See RFC 2396 Section 3.2.2. 1462 * 1463 * 1464 * @param p_address The address string to check 1465 * @return true if the string is a syntactically valid IPv4 address 1466 * or hostname 1467 */ 1468 public static boolean isWellFormedAddress(String p_address) 1469 { 1470 1471 if (p_address == null) 1472 { 1473 return false; 1474 } 1475 1476 String address = p_address.trim(); 1477 int addrLength = address.length(); 1478 1479 if (addrLength == 0 || addrLength > 255) 1480 { 1481 return false; 1482 } 1483 1484 if (address.startsWith(".") || address.startsWith("-")) 1485 { 1486 return false; 1487 } 1488 1489 // rightmost domain label starting with digit indicates IP address 1490 // since top level domain label can only start with an alpha 1491 // see RFC 2396 Section 3.2.2 1492 int index = address.lastIndexOf('.'); 1493 1494 if (address.endsWith(".")) 1495 { 1496 index = address.substring(0, index).lastIndexOf('.'); 1497 } 1498 1499 if (index + 1 < addrLength && isDigit(p_address.charAt(index + 1))) 1500 { 1501 char testChar; 1502 int numDots = 0; 1503 1504 // make sure that 1) we see only digits and dot separators, 2) that 1505 // any dot separator is preceded and followed by a digit and 1506 // 3) that we find 3 dots 1507 for (int i = 0; i < addrLength; i++) 1508 { 1509 testChar = address.charAt(i); 1510 1511 if (testChar == '.') 1512 { 1513 if (!isDigit(address.charAt(i - 1)) 1514 || (i + 1 < addrLength &&!isDigit(address.charAt(i + 1)))) 1515 { 1516 return false; 1517 } 1518 1519 numDots++; 1520 } 1521 else if (!isDigit(testChar)) 1522 { 1523 return false; 1524 } 1525 } 1526 1527 if (numDots != 3) 1528 { 1529 return false; 1530 } 1531 } 1532 else 1533 { 1534 1535 // domain labels can contain alphanumerics and '-" 1536 // but must start and end with an alphanumeric 1537 char testChar; 1538 1539 for (int i = 0; i < addrLength; i++) 1540 { 1541 testChar = address.charAt(i); 1542 1543 if (testChar == '.') 1544 { 1545 if (!isAlphanum(address.charAt(i - 1))) 1546 { 1547 return false; 1548 } 1549 1550 if (i + 1 < addrLength &&!isAlphanum(address.charAt(i + 1))) 1551 { 1552 return false; 1553 } 1554 } 1555 else if (!isAlphanum(testChar) && testChar != '-') 1556 { 1557 return false; 1558 } 1559 } 1560 } 1561 1562 return true; 1563 } 1564 1565 /** 1566 * Determine whether a char is a digit. 1567 * 1568 * 1569 * @param p_char the character to check 1570 * @return true if the char is betweeen '0' and '9', false otherwise 1571 */ 1572 private static boolean isDigit(char p_char) 1573 { 1574 return p_char >= '0' && p_char <= '9'; 1575 } 1576 1577 /** 1578 * Determine whether a character is a hexadecimal character. 1579 * 1580 * 1581 * @param p_char the character to check 1582 * @return true if the char is betweeen '0' and '9', 'a' and 'f' 1583 * or 'A' and 'F', false otherwise 1584 */ 1585 private static boolean isHex(char p_char) 1586 { 1587 return (isDigit(p_char) || (p_char >= 'a' && p_char <= 'f') 1588 || (p_char >= 'A' && p_char <= 'F')); 1589 } 1590 1591 /** 1592 * Determine whether a char is an alphabetic character: a-z or A-Z 1593 * 1594 * 1595 * @param p_char the character to check 1596 * @return true if the char is alphabetic, false otherwise 1597 */ 1598 private static boolean isAlpha(char p_char) 1599 { 1600 return ((p_char >= 'a' && p_char <= 'z') 1601 || (p_char >= 'A' && p_char <= 'Z')); 1602 } 1603 1604 /** 1605 * Determine whether a char is an alphanumeric: 0-9, a-z or A-Z 1606 * 1607 * 1608 * @param p_char the character to check 1609 * @return true if the char is alphanumeric, false otherwise 1610 */ 1611 private static boolean isAlphanum(char p_char) 1612 { 1613 return (isAlpha(p_char) || isDigit(p_char)); 1614 } 1615 1616 /** 1617 * Determine whether a character is a reserved character: 1618 * ';', '/', '?', ':', '@', '&', '=', '+', '$' or ',' 1619 * 1620 * 1621 * @param p_char the character to check 1622 * @return true if the string contains any reserved characters 1623 */ 1624 private static boolean isReservedCharacter(char p_char) 1625 { 1626 return RESERVED_CHARACTERS.indexOf(p_char) != -1; 1627 } 1628 1629 /** 1630 * Determine whether a char is an unreserved character. 1631 * 1632 * 1633 * @param p_char the character to check 1634 * @return true if the char is unreserved, false otherwise 1635 */ 1636 private static boolean isUnreservedCharacter(char p_char) 1637 { 1638 return (isAlphanum(p_char) || MARK_CHARACTERS.indexOf(p_char) != -1); 1639 } 1640 1641 /** 1642 * Determine whether a given string contains only URI characters (also 1643 * called "uric" in RFC 2396). uric consist of all reserved 1644 * characters, unreserved characters and escaped characters. 1645 * 1646 * 1647 * @param p_uric URI string 1648 * @return true if the string is comprised of uric, false otherwise 1649 */ 1650 private static boolean isURIString(String p_uric) 1651 { 1652 1653 if (p_uric == null) 1654 { 1655 return false; 1656 } 1657 1658 int end = p_uric.length(); 1659 char testChar = '\0'; 1660 1661 for (int i = 0; i < end; i++) 1662 { 1663 testChar = p_uric.charAt(i); 1664 1665 if (testChar == '%') 1666 { 1667 if (i + 2 >= end ||!isHex(p_uric.charAt(i + 1)) 1668 ||!isHex(p_uric.charAt(i + 2))) 1669 { 1670 return false; 1671 } 1672 else 1673 { 1674 i += 2; 1675 1676 continue; 1677 } 1678 } 1679 1680 if (isReservedCharacter(testChar) || isUnreservedCharacter(testChar)) 1681 { 1682 continue; 1683 } 1684 else 1685 { 1686 return false; 1687 } 1688 } 1689 1690 return true; 1691 } 1692 }