public static String encode(String s) {
    if (s == null) {
      return null;
    }

    StringBuilder sb = null;

    int startingIndex = 0;
    boolean hasReservedChars = false;

    // scan through to see where we have to start % encoding, if at all
    while (startingIndex < s.length() && !hasReservedChars) {
      if (!isUnreserved(s.charAt(startingIndex))) {
        hasReservedChars = true;
      } else {
        startingIndex += 1;
      }
    }

    if (hasReservedChars && startingIndex < s.length()) {
      sb = new StringBuilder(s.length() + 40);
      sb.append(s.substring(0, startingIndex));

      byte[] byteArray = s.substring(startingIndex).getBytes(UTF_8_CHARSET);
      for (int i = 0; i < byteArray.length; i++) {
        byte bite = byteArray[i];
        if (isUnreserved(bite)) {
          sb.append((char)bite);
        } else {
          // turn the Byte into an int into the hex string, but be sure to mask out the unneeded bits
          // to avoid nastiness with converting to a negative int
          sb.append('%');
          sb.append(HEX_DIGITS[(bite >> 4) & 0x0F]);
          sb.append(HEX_DIGITS[bite & 0x0F]);
        }
      }
    }

    return (sb == null) ? s : sb.toString();
  }