1 /*
   2  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.xml.internal.rngom.util;
  27 
  28 import java.net.URL;
  29 import java.net.MalformedURLException;
  30 import java.io.UnsupportedEncodingException;
  31 
  32 public class Uri {
  33   private static String utf8 = "UTF-8";
  34 
  35   public static boolean isValid(String s) {
  36     return isValidPercent(s) && isValidFragment(s) && isValidScheme(s);
  37   }
  38 
  39   private static final String HEX_DIGITS = "0123456789abcdef";
  40 
  41   public static String escapeDisallowedChars(String s) {
  42     StringBuffer buf = null;
  43     int len = s.length();
  44     int done = 0;
  45     for (;;) {
  46       int i = done;
  47       for (;;) {
  48         if (i == len) {
  49           if (done == 0)
  50             return s;
  51           break;
  52         }
  53         if (isExcluded(s.charAt(i)))
  54           break;
  55         i++;
  56       }
  57       if (buf == null)
  58         buf = new StringBuffer();
  59       if (i > done) {
  60         buf.append(s.substring(done, i));
  61         done = i;
  62       }
  63       if (i == len)
  64         break;
  65       for (i++; i < len && isExcluded(s.charAt(i)); i++)
  66         ;
  67       String tem = s.substring(done, i);
  68       byte[] bytes;
  69       try {
  70         bytes = tem.getBytes(utf8);
  71       }
  72       catch (UnsupportedEncodingException e) {
  73         utf8 = "UTF8";
  74         try {
  75           bytes = tem.getBytes(utf8);
  76         }
  77         catch (UnsupportedEncodingException e2) {
  78           // Give up
  79           return s;
  80         }
  81       }
  82       for (int j = 0; j < bytes.length; j++) {
  83         buf.append('%');
  84         buf.append(HEX_DIGITS.charAt((bytes[j] & 0xFF) >> 4));
  85         buf.append(HEX_DIGITS.charAt(bytes[j] & 0xF));
  86       }
  87       done = i;
  88     }
  89     return buf.toString();
  90   }
  91 
  92   private static final String excluded = "<>\"{}|\\^`";
  93 
  94   private static boolean isExcluded(char c) {
  95     return c <= 0x20 || c >= 0x7F || excluded.indexOf(c) >= 0;
  96   }
  97 
  98   private static boolean isAlpha(char c) {
  99     return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
 100   }
 101 
 102   private static boolean isHexDigit(char c) {
 103     return ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F') || isDigit(c);
 104   }
 105 
 106   private static boolean isDigit(char c) {
 107     return '0' <= c && c <= '9';
 108   }
 109 
 110   private static boolean isSchemeChar(char c) {
 111     return isAlpha(c) || isDigit(c) || c == '+' || c == '-' || c =='.';
 112   }
 113 
 114   private static boolean isValidPercent(String s) {
 115     int len = s.length();
 116     for (int i = 0; i < len; i++)
 117       if (s.charAt(i) == '%') {
 118         if (i + 2 >= len)
 119           return false;
 120         else if (!isHexDigit(s.charAt(i + 1))
 121                  || !isHexDigit(s.charAt(i + 2)))
 122           return false;
 123       }
 124     return true;
 125   }
 126 
 127   private static boolean isValidFragment(String s) {
 128     int i = s.indexOf('#');
 129     return i < 0 || s.indexOf('#', i + 1) < 0;
 130   }
 131 
 132   private static boolean isValidScheme(String s) {
 133     if (!isAbsolute(s))
 134       return true;
 135     int i = s.indexOf(':');
 136     if (i == 0
 137         || i + 1 == s.length()
 138         || !isAlpha(s.charAt(0)))
 139       return false;
 140     while (--i > 0)
 141       if (!isSchemeChar(s.charAt(i)))
 142         return false;
 143     return true;
 144   }
 145 
 146   public static String resolve(String baseUri, String uriReference) {
 147     if (!isAbsolute(uriReference) && baseUri != null && isAbsolute(baseUri)) {
 148       try {
 149         return new URL(new URL(baseUri), uriReference).toString();
 150       }
 151       catch (MalformedURLException e) { }
 152     }
 153     return uriReference;
 154   }
 155 
 156   public static boolean hasFragmentId(String uri) {
 157     return uri.indexOf('#') >= 0;
 158   }
 159 
 160   public static boolean isAbsolute(String uri) {
 161     int i = uri.indexOf(':');
 162     if (i < 0)
 163       return false;
 164     while (--i >= 0) {
 165       switch (uri.charAt(i)) {
 166       case '#':
 167       case '/':
 168       case '?':
 169         return false;
 170       }
 171     }
 172     return true;
 173   }
 174 }