Clover coverage report - Clover results for XOM 1.2d1
Coverage timestamp: Wed Feb 8 2006 08:31:33 EST
file stats: LOC: 682   Methods: 11
NCLOC: 528   Classes: 2
 
 Source file Conditionals Statements Methods TOTAL
URIUtil.java 98.8% 99.3% 100% 99.3%
coverage coverage
 1    /* Copyright 2004, 2005 Elliotte Rusty Harold
 2   
 3    This library is free software; you can redistribute it and/or modify
 4    it under the terms of version 2.1 of the GNU Lesser General Public
 5    License as published by the Free Software Foundation.
 6   
 7    This library is distributed in the hope that it will be useful,
 8    but WITHOUT ANY WARRANTY; without even the implied warranty of
 9    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 10    GNU Lesser General Public License for more details.
 11   
 12    You should have received a copy of the GNU Lesser General Public
 13    License along with this library; if not, write to the
 14    Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 15    Boston, MA 02111-1307 USA
 16   
 17    You can contact Elliotte Rusty Harold by sending e-mail to
 18    elharo@metalab.unc.edu. Please include the word "XOM" in the
 19    subject line. The XOM home page is located at http://www.xom.nu/
 20    */
 21   
 22    package nu.xom;
 23   
 24    import java.io.UnsupportedEncodingException;
 25   
 26   
 27    /**
 28    * These methods are not fully general.
 29    * You would need to uncomment some lines to make this a
 30    * public API. Certain preconditons for these methods to
 31    * operate correctly are true in the context of XOM,
 32    * but may well not be true in a more general context.
 33    *
 34    * @author Elliotte Rusty Harold
 35    * @version 1.1b4
 36    *
 37    */
 38    class URIUtil {
 39   
 40    // We assume the URI has already been verified as a potentially
 41    // legal URI. Thus we don't have to check everything here.
 42  51 static boolean isOpaque(String uri) {
 43   
 44  51 int colon = uri.indexOf(':');
 45    // if (colon < 1) return false;
 46    // This next line is the difference between absolute and opaque
 47  29 if (uri.substring(colon+1).startsWith("/")) return false;
 48  1 if (!Verifier.isAlpha(uri.charAt(0))) return false;
 49    /* for (int i = 1; i < colon; i++) {
 50    if (!Verifier.isSchemeCharacter(uri.charAt(i))) {
 51    return false;
 52    }
 53    } */
 54  21 return true;
 55   
 56    }
 57   
 58   
 59  229374 static boolean isAbsolute(String uri) {
 60   
 61  229374 int colon = uri.indexOf(':');
 62  2124 if (colon < 1) return false;
 63    // We assume the URI has already been verified as a potentially
 64    // legal URI. Thus we don't have to check everything here.
 65    /*if (!Verifier.isAlpha(uri.charAt(0))) return false;
 66    for (int i = 1; i < colon; i++) {
 67    if (!Verifier.isSchemeCharacter(uri.charAt(i))) return false;
 68    } */
 69  227250 return true;
 70   
 71    }
 72   
 73   
 74    // This doesn't do enough error checking to be a public API.
 75  226861 static String absolutize(String baseURI, String spec) {
 76   
 77  54 if ("".equals(baseURI) || baseURI == null) return spec;
 78   
 79  226807 ParsedURI base = new ParsedURI(baseURI);
 80   
 81    // This seems to be necessary to handle base URLs like
 82    // http://www.example.com/test/data/..
 83    // but I don't think it's part of the 3986 algorithm.
 84    // ???? It may be a bug in that algorithm. Check.
 85  2 if (base.path.endsWith("/..")) base.path += '/';
 86   
 87    // The variable names R and T violate Java naming conventions.
 88    // They are taken from the pseudo-code in the RFC 3986 spec.
 89  226807 ParsedURI R = new ParsedURI(spec);
 90  226807 ParsedURI T = new ParsedURI();
 91   
 92    // We should be able to skip this check. basically it
 93    // asserts that the spec is not an absolute URI already
 94    /* if (R.scheme != null) {
 95    T.scheme = R.scheme;
 96    T.authority = R.authority;
 97    T.query = R.query;
 98    T.path = removeDotSegments(R.path);
 99    }
 100    else { */
 101  226807 if (R.authority != null) {
 102  1 T.authority = R.authority;
 103  1 T.query = R.query;
 104  1 T.path = removeDotSegments(R.path);
 105    }
 106    else {
 107  226806 if ("".equals(R.path)) {
 108  224786 T.path = base.path;
 109  224786 if (R.query != null) {
 110  1 T.query = R.query;
 111    }
 112    else {
 113  224785 T.query = base.query;
 114    }
 115    }
 116    else {
 117  2020 if (R.path.startsWith("/")) {
 118  4 T.path = removeDotSegments(R.path);
 119    }
 120    else {
 121  2016 T.path = merge(base, R.path);
 122  2016 T.path = removeDotSegments(T.path);
 123    }
 124  2020 T.query = R.query;
 125    }
 126  226806 T.authority = base.authority;
 127    }
 128  226807 T.scheme = base.scheme;
 129    // }
 130    // Fragment ID of base URI is never considered
 131  226807 T.fragment = R.fragment;
 132   
 133  226807 return T.toString();
 134   
 135    }
 136   
 137   
 138  2016 private static String merge(ParsedURI base, String relativePath) {
 139   
 140  2016 if (base.authority != null && "".equals(base.path)
 141    && !"".equals(base.authority)) {
 142  2 return "/" + relativePath;
 143    }
 144   
 145  2014 int lastSlash = base.path.lastIndexOf('/');
 146  1 if (lastSlash == -1) return relativePath;
 147  2013 String topPath = base.path.substring(0, lastSlash+1);
 148  2013 return topPath + relativePath;
 149   
 150    }
 151   
 152   
 153  2021 private static String removeDotSegments(String path) {
 154   
 155  2021 StringBuffer output = new StringBuffer();
 156   
 157  2021 while (path.length() > 0) {
 158  20086 if (path.startsWith("/./")) {
 159  8 path = '/' + path.substring(3);
 160    }
 161  20078 else if (path.equals("/.")) {
 162  4 path = "/";
 163    }
 164  20074 else if (path.startsWith("/../")) {
 165  65 path = '/' + path.substring(4);
 166  65 int lastSlash = output.toString().lastIndexOf('/');
 167  58 if (lastSlash != -1) output.setLength(lastSlash);
 168    }
 169  20009 else if (path.equals("/..")) {
 170  5 path = "/";
 171  5 int lastSlash = output.toString().lastIndexOf('/');
 172  3 if (lastSlash != -1) output.setLength(lastSlash);
 173    }
 174    // These next three cases are unreachable in the context of XOM.
 175    // They may be needed in a more general public URIUtil.
 176    /* else if (path.equals(".") || path.equals("..")) {
 177    path = "";
 178    }
 179    else if (path.startsWith("../")) {
 180    path = path.substring(3);
 181    }
 182    else if (path.startsWith("./")) {
 183    path = path.substring(2);
 184    } */
 185    else {
 186  20004 int nextSlash = path.indexOf('/');
 187  20003 if (nextSlash == 0) nextSlash = path.indexOf('/', 1);
 188  20004 if (nextSlash == -1) {
 189  2020 output.append(path);
 190  2020 path = "";
 191    }
 192    else {
 193  17984 output.append(path.substring(0, nextSlash));
 194  17984 path = path.substring(nextSlash);
 195    }
 196    }
 197    }
 198   
 199  2021 return output.toString();
 200   
 201    }
 202   
 203   
 204    // really just a struct
 205    static class ParsedURI {
 206   
 207    String scheme;
 208    String schemeSpecificPart;
 209    String query;
 210    String fragment;
 211    String authority;
 212    String path = "";
 213   
 214  515955 ParsedURI(String spec) {
 215   
 216  515955 int colon = spec.indexOf(':');
 217  515955 int question;
 218   
 219    // URIs can only contain one sharp sign
 220  515955 int sharp = spec.lastIndexOf('#');
 221   
 222    // Fragment IDs can contain question marks so we only read
 223    // the question mark before the fragment ID, if any
 224  515888 if (sharp == -1) question = spec.indexOf('?');
 225  67 else question = spec.substring(0, sharp).indexOf('?');
 226   
 227  287038 if (colon != -1) scheme = spec.substring(0, colon);
 228   
 229  515955 if (question == -1 && sharp == -1) {
 230  514777 schemeSpecificPart = spec.substring(colon+1);
 231    }
 232  1178 else if (question != -1) {
 233  1129 if (question < colon) {
 234  1 MalformedURIException ex
 235    = new MalformedURIException("Unparseable URI");
 236  1 ex.setData(spec);
 237  1 throw ex;
 238    }
 239  1128 schemeSpecificPart = spec.substring(colon+1, question);
 240    }
 241    else {
 242  49 if (sharp < colon) {
 243  1 MalformedURIException ex
 244    = new MalformedURIException("Unparseable URI");
 245  1 ex.setData(spec);
 246  1 throw ex;
 247    }
 248  48 schemeSpecificPart = spec.substring(colon+1, sharp);
 249    }
 250   
 251  515953 if (sharp != -1) {
 252  66 fragment = spec.substring(sharp+1);
 253    }
 254   
 255  515953 if (question != -1) {
 256  1128 if (sharp == -1) {
 257  1110 query = spec.substring(question+1);
 258    }
 259    else {
 260  18 query = spec.substring(question+1, sharp);
 261    }
 262    }
 263   
 264  515953 if (schemeSpecificPart.startsWith("//")) {
 265  285716 int authorityBegin = 2;
 266  285716 int authorityEnd = schemeSpecificPart.indexOf('/', authorityBegin);
 267  285716 if (authorityEnd == -1) {
 268  557 authority = schemeSpecificPart.substring(2);
 269  557 path = "";
 270    }
 271    else {
 272  285159 authority = schemeSpecificPart.substring(authorityBegin, authorityEnd);
 273  285159 path = schemeSpecificPart.substring(authorityEnd);
 274    }
 275    }
 276    else {
 277  230237 path = schemeSpecificPart;
 278    }
 279   
 280    }
 281   
 282  226807 ParsedURI() {}
 283   
 284  226807 public String toString() {
 285   
 286  226807 StringBuffer result = new StringBuffer(30);
 287   
 288  226807 if (scheme != null) {
 289  226806 result.append(scheme);
 290  226806 result.append(':');
 291    }
 292   
 293  226807 if (schemeSpecificPart != null) {
 294  0 result.append(schemeSpecificPart);
 295    }
 296    else {
 297  226807 result.append("//");
 298  226575 if (authority != null) result.append(authority);
 299  226807 result.append(path);
 300    }
 301   
 302  226807 if (query != null) {
 303  8 result.append('?');
 304  8 result.append(query);
 305    }
 306   
 307  226807 if (fragment != null) {
 308  6 result.append('#');
 309  6 result.append(fragment);
 310    }
 311   
 312  226807 return result.toString();
 313   
 314    }
 315   
 316    }
 317   
 318   
 319  2175 static String toURI(String iri) {
 320   
 321  2175 int length = iri.length();
 322  2175 StringBuffer uri = new StringBuffer(length);
 323  2175 for (int i = 0; i < length; i++) {
 324  39364 char c = iri.charAt(i);
 325  39364 switch(c) {
 326  1 case ' ':
 327  1 uri.append("%20");
 328  1 break;
 329  3 case '!':
 330  3 uri.append(c);
 331  3 break;
 332  2 case '"':
 333  2 uri.append("%22");
 334  2 break;
 335  9 case '#':
 336  9 uri.append(c);
 337  9 break;
 338  5 case '$':
 339  5 uri.append(c);
 340  5 break;
 341  15 case '%':
 342  15 uri.append(c);
 343  15 break;
 344  2 case '&':
 345  2 uri.append(c);
 346  2 break;
 347  3 case '\'':
 348  3 uri.append(c);
 349  3 break;
 350  2 case '(':
 351  2 uri.append(c);
 352  2 break;
 353  2 case ')':
 354  2 uri.append(c);
 355  2 break;
 356  4 case '*':
 357  4 uri.append(c);
 358  4 break;
 359  8 case '+':
 360  8 uri.append(c);
 361  8 break;
 362  3 case ',':
 363  3 uri.append(c);
 364  3 break;
 365  332 case '-':
 366  332 uri.append(c);
 367  332 break;
 368  2394 case '.':
 369  2394 uri.append(c);
 370  2394 break;
 371  2952 case '/':
 372  2952 uri.append(c);
 373  2952 break;
 374  1795 case '0':
 375  1795 uri.append(c);
 376  1795 break;
 377  1238 case '1':
 378  1238 uri.append(c);
 379  1238 break;
 380  888 case '2':
 381  888 uri.append(c);
 382  888 break;
 383  567 case '3':
 384  567 uri.append(c);
 385  567 break;
 386  470 case '4':
 387  470 uri.append(c);
 388  470 break;
 389  609 case '5':
 390  609 uri.append(c);
 391  609 break;
 392  668 case '6':
 393  668 uri.append(c);
 394  668 break;
 395  250 case '7':
 396  250 uri.append(c);
 397  250 break;
 398  328 case '8':
 399  328 uri.append(c);
 400  328 break;
 401  308 case '9':
 402  308 uri.append(c);
 403  308 break;
 404  219 case ':':
 405  219 uri.append(c);
 406  219 break;
 407  17 case ';':
 408  17 uri.append(c);
 409  17 break;
 410  2 case '<':
 411  2 uri.append("%3C");
 412  2 break;
 413  21 case '=':
 414  21 uri.append(c);
 415  21 break;
 416  1 case '>':
 417  1 uri.append("%3E");
 418  1 break;
 419  17 case '?':
 420  17 uri.append(c);
 421  17 break;
 422  14 case '@':
 423  14 uri.append(c);
 424  14 break;
 425  20 case 'A':
 426  20 uri.append(c);
 427  20 break;
 428  9 case 'B':
 429  9 uri.append(c);
 430  9 break;
 431  16 case 'C':
 432  16 uri.append(c);
 433  16 break;
 434  8 case 'D':
 435  8 uri.append(c);
 436  8 break;
 437  23 case 'E':
 438  23 uri.append(c);
 439  23 break;
 440  23 case 'F':
 441  23 uri.append(c);
 442  23 break;
 443  5 case 'G':
 444  5 uri.append(c);
 445  5 break;
 446  8 case 'H':
 447  8 uri.append(c);
 448  8 break;
 449  14 case 'I':
 450  14 uri.append(c);
 451  14 break;
 452  2 case 'J':
 453  2 uri.append(c);
 454  2 break;
 455  4 case 'K':
 456  4 uri.append(c);
 457  4 break;
 458  11 case 'L':
 459  11 uri.append(c);
 460  11 break;
 461  18 case 'M':
 462  18 uri.append(c);
 463  18 break;
 464  14 case 'N':
 465  14 uri.append(c);
 466  14 break;
 467  16 case 'O':
 468  16 uri.append(c);
 469  16 break;
 470  607 case 'P':
 471  607 uri.append(c);
 472  607 break;
 473  3 case 'Q':
 474  3 uri.append(c);
 475  3 break;
 476  54 case 'R':
 477  54 uri.append(c);
 478  54 break;
 479  12 case 'S':
 480  12 uri.append(c);
 481  12 break;
 482  60 case 'T':
 483  60 uri.append(c);
 484  60 break;
 485  9 case 'U':
 486  9 uri.append(c);
 487  9 break;
 488  3 case 'V':
 489  3 uri.append(c);
 490  3 break;
 491  13 case 'W':
 492  13 uri.append(c);
 493  13 break;
 494  16 case 'X':
 495  16 uri.append(c);
 496  16 break;
 497  2 case 'Y':
 498  2 uri.append(c);
 499  2 break;
 500  5 case 'Z':
 501  5 uri.append(c);
 502  5 break;
 503  19 case '[':
 504  19 uri.append(c);
 505  19 break;
 506  2 case '\\':
 507  2 uri.append("%5C");
 508  2 break;
 509  20 case ']':
 510  20 uri.append(c);
 511  20 break;
 512  3 case '^':
 513  3 uri.append("%5E");
 514  3 break;
 515  16 case '_':
 516  16 uri.append(c);
 517  16 break;
 518  2 case '`':
 519  2 uri.append("%60");
 520  2 break;
 521  2659 case 'a':
 522  2659 uri.append(c);
 523  2659 break;
 524  671 case 'b':
 525  671 uri.append(c);
 526  671 break;
 527  221 case 'c':
 528  221 uri.append(c);
 529  221 break;
 530  1629 case 'd':
 531  1629 uri.append(c);
 532  1629 break;
 533  619 case 'e':
 534  619 uri.append(c);
 535  619 break;
 536  112 case 'f':
 537  112 uri.append(c);
 538  112 break;
 539  148 case 'g':
 540  148 uri.append(c);
 541  148 break;
 542  216 case 'h':
 543  216 uri.append(c);
 544  216 break;
 545  2905 case 'i':
 546  2905 uri.append(c);
 547  2905 break;
 548  25 case 'j':
 549  25 uri.append(c);
 550  25 break;
 551  46 case 'k':
 552  46 uri.append(c);
 553  46 break;
 554  3749 case 'l':
 555  3749 uri.append(c);
 556  3749 break;
 557  2844 case 'm':
 558  2844 uri.append(c);
 559  2844 break;
 560  858 case 'n':
 561  858 uri.append(c);
 562  858 break;
 563  526 case 'o':
 564  526 uri.append(c);
 565  526 break;
 566  873 case 'p':
 567  873 uri.append(c);
 568  873 break;
 569  15 case 'q':
 570  15 uri.append(c);
 571  15 break;
 572  234 case 'r':
 573  234 uri.append(c);
 574  234 break;
 575  1429 case 's':
 576  1429 uri.append(c);
 577  1429 break;
 578  1015 case 't':
 579  1015 uri.append(c);
 580  1015 break;
 581  131 case 'u':
 582  131 uri.append(c);
 583  131 break;
 584  1877 case 'v':
 585  1877 uri.append(c);
 586  1877 break;
 587  227 case 'w':
 588  227 uri.append(c);
 589  227 break;
 590  2090 case 'x':
 591  2090 uri.append(c);
 592  2090 break;
 593  43 case 'y':
 594  43 uri.append(c);
 595  43 break;
 596  7 case 'z':
 597  7 uri.append(c);
 598  7 break;
 599  2 case '{':
 600  2 uri.append("%7B");
 601  2 break;
 602  2 case '|':
 603  2 uri.append("%7C");
 604  2 break;
 605  2 case '}':
 606  2 uri.append("%7D");
 607  2 break;
 608  2 case '~':
 609  2 uri.append(c);
 610  2 break;
 611  1 default:
 612  1 uri.append(percentEscape(c));
 613    }
 614    }
 615  2175 return uri.toString();
 616   
 617    }
 618   
 619   
 620  4 static String percentEscape(char c) {
 621   
 622  4 StringBuffer result = new StringBuffer(3);
 623  4 String s = String.valueOf(c);
 624  4 try {
 625  4 byte[] data = s.getBytes("UTF8");
 626  4 for (int i = 0; i < data.length; i++) {
 627  6 result.append('%');
 628  6 String hex = Integer.toHexString(data[i]).toUpperCase();
 629  6 if (c < 16) {
 630  1 result.append('0');
 631  1 result.append(hex);
 632    }
 633    else {
 634    // When c is negative as a byte, (e.g. greater
 635    // than 128) the hex strings come out as 8
 636    // characters rather than 2.
 637  5 result.append(hex.substring(hex.length()-2));
 638    }
 639    }
 640  4 return result.toString();
 641    }
 642    catch (UnsupportedEncodingException ex) {
 643  0 throw new RuntimeException(
 644    "Broken VM: does not recognize UTF-8 encoding");
 645    }
 646   
 647    }
 648   
 649   
 650  259 static String relativize(String base, String abs) {
 651   
 652  259 try {
 653  259 ParsedURI parsedBase = new ParsedURI(base);
 654  259 ParsedURI parsedAbs = new ParsedURI(abs);
 655   
 656  259 if (parsedBase.scheme.equals(parsedAbs.scheme)
 657    && parsedBase.authority.equals(parsedAbs.authority)) {
 658   
 659   
 660  231 String basePath = parsedBase.path;
 661  231 String relPath = parsedAbs.path;
 662   
 663  231 while (basePath.length() > 1) {
 664  235 basePath = basePath.substring(0, basePath.lastIndexOf('/'));
 665  235 if (relPath.startsWith(basePath)) {
 666  229 return relPath.substring(basePath.length()+1);
 667    }
 668    }
 669   
 670  2 return relPath;
 671    }
 672    else {
 673  28 return abs;
 674    }
 675    }
 676    catch (Exception ex) {
 677  0 return abs;
 678    }
 679    }
 680   
 681   
 682    }