gt:                             throw new IllegalArgumentException();
context: /*******************************************************************************
   * Copyright (c) 1997, 2018 IBM Corporation and others.
   * All rights reserved. This program and the accompanying materials
   * are made available under the terms of the Eclipse Public License v1.0
   * which accompanies this distribution, and is available at
   * http://www.eclipse.org/legal/epl-v10.html
   *
   * Contributors:
   *     IBM Corporation - initial API and implementation
   *******************************************************************************/
  package com.ibm.wsspi.webcontainer.util;
  
  import java.io.UnsupportedEncodingException;
  import java.net.URLDecoder;
  import java.text.MessageFormat;
  import java.util.HashSet;
  import java.util.Hashtable;
  import java.util.logging.Level;
  import java.util.logging.Logger;
  
  import javax.servlet.http.HttpServletRequest;
  
  import com.ibm.ejs.ras.TraceNLS;
  import com.ibm.ws.webcontainer.webapp.WebAppRequestDispatcher;
  import com.ibm.wsspi.webcontainer.WCCustomProperties;
  import com.ibm.wsspi.webcontainer.logging.LoggerFactory;
  
  /**
   *
   * 
   * RequestUtils provides methods for retrieving various data based on the current
   * request such as the parsing the query string and retrieving the current uri
   * depending on dispatch type.
   * 
   * @ibm-private-in-use
   * 
   * @since   WAS7.0
   *
   */
  public class RequestUtils {
      private static final Logger logger = LoggerFactory.getInstance().getLogger("com.ibm.ws.webcontainer.util");
      private static final String CLASS_NAME="com.ibm.wsspi.webcontainer.util.RequestUtils";
      private static final TraceNLS nls = TraceNLS.getTraceNLS(RequestUtils.class, "com.ibm.ws.webcontainer.resources.Messages"); //724365.4
  
      private static final String SHORT_ENGLISH = "8859_1"; //a shortened ASCII encoding
  	
      private static final boolean ignoreInvalidQueryString = WCCustomProperties.IGNORE_INVALID_QUERY_STRING;       //PK75617
      private static final boolean allowQueryParamWithNoEqual = WCCustomProperties.ALLOW_QUERY_PARAM_WITH_NO_EQUAL;       //PM35450
      private static final String EMPTY_STRING = ""; //PM35450
      private static final int maxParamPerRequest = WCCustomProperties.MAX_PARAM_PER_REQUEST; // PM53930 (724365)
      private static final int maxDuplicateHashKeyParams = WCCustomProperties.MAX_DUPLICATE_HASHKEY_PARAMS; // PM58495 (728397)
      private static final boolean decodeParamViaReqEncoding = WCCustomProperties.DECODE_PARAM_VIA_REQ_ENCODING; // PM92940
      private static final boolean printbyteValueandcharParamdata = WCCustomProperties.PRINT_BYTEVALUE_AND_CHARPARAMDATA; //PM92940
  
     /**
      *
      * Parses a query string passed from the client to the
      * server and builds a <code>HashTable</code> object
      * with key-value pairs.
      * The query string should be in the form of a string
      * packaged by the GET or POST method, that is, it
      * should have key-value pairs in the form <i>key=value</i>,
      * with each pair separated from the next by a & character.
      *
      * <p>A key can appear more than once in the query string
      * with different values. However, the key appears only once in
      * the hashtable, with its value being
      * an array of strings containing the multiple values sent
      * by the query string.
      *
      * <p>The keys and values in the hashtable are stored in their
      * decoded form, so
      * any + characters are converted to spaces, and characters
      * sent in hexadecimal notation (like <i>%xx</i>) are
      * converted to ASCII characters.
      *
      * @param s		a string containing the query to be parsed
      *
      * @return		a <code>HashTable</code> object built
      * 			from the parsed key-value pairs
      *
      * @exception IllegalArgumentException	if the query string
      *						is invalid
      *
      */
      @SuppressWarnings("rawtypes")
      static public Hashtable parseQueryString(String s) {
          return parseQueryString(new StringQueryString(s), SHORT_ENGLISH);
      }
  
      @SuppressWarnings("rawtypes")
      static public Hashtable parseQueryString(String s, String encoding) {
          return parseQueryString(new StringQueryString(s), encoding);
      }
  
      private static String lastShortEnglishEncoding = null;
  
      private static boolean isShortEnglishEncoding(String encoding) {
          if (encoding == lastShortEnglishEncoding
              || encoding == SHORT_ENGLISH
              || encoding == "ISO-8859-1")
              return true;
  
          if (encoding.endsWith("8859_1")
              || encoding.endsWith("8859-1")
              || encoding.indexOf("8859-1-Windows") != -1) {
              lastShortEnglishEncoding = encoding;
              return true;
          }
          return false;
      }
  
      private static abstract class QueryString {
          protected int pair_start = 0, equalSign = -1, i = -1;
          protected boolean isKeySingleByteString = true;
          protected boolean isValueSingleByteString = true;
          protected boolean equalsFound = false;
  
          abstract protected boolean lessThanEqualLength();
  
          abstract protected boolean isEqualLength();
  
          abstract protected char getNextChar();
  
          protected void increment() {
              i++;
          }
  
          protected void setEqualSign() {
              equalSign = i;
          }
  
          boolean findNextPair() {
              increment();
              pair_start = i;
              isKeySingleByteString = true;
              isValueSingleByteString = true;
              equalsFound = false;
              for (; lessThanEqualLength(); increment()) {
                  if (isEqualLength()) {
                      if (!equalsFound) {
                          setEqualSign();
                      }
                      return true;
                  }
                  char c = getNextChar();
                  if (c == '&') {
                      if (!equalsFound) {
                          setEqualSign();
                      }
                      return true;
                  }
                  // Only record the first = after pair_start so we handle if a value has an = in it.
                  if (c == '=' && !equalsFound) {
                      setEqualSign();
                      equalsFound = true;
                  }
                  if (!decodeParamViaReqEncoding && c > '\u007F') {
                      if (equalsFound) {
                          isValueSingleByteString = false;
                      } else {
                          isKeySingleByteString = false;
                      }
                  }
              }
              return false;
          }
  
          final boolean isKeySingleByteString() {
              return isKeySingleByteString;
          }
  
          final boolean isValueSingleByteString() {
              return isValueSingleByteString;
          }
  
          final boolean hasEquals() {
              return equalsFound;
          }
  
          abstract String getKey();
  
          abstract String getValue();
  
          abstract String parseKey();
  
          abstract String parseValue();
  
          /*
           * Parse a name in the query string.
           */
          // @MD17415 begin part 3 of 3 New Loop for finding key and value
          protected String parseName(final char[] ch, final int startOffset, final int endOffset, boolean isKey) {
              int j = 0;
              char[] c = null;
              for (int offset = startOffset; offset < endOffset; offset++) {
                  switch (ch[offset]) {
                      case '+':
                          if (c == null) {
                              c = new char[endOffset - startOffset];
                              j = offset - startOffset;
                              if (j != 0) {
                                  System.arraycopy(ch, startOffset, c, 0, j);
                              }
                          }
                          c[j++] = ' ';
                          break;
                      case '%':
                          if (offset + 2 < endOffset) { // @RWS2
                              if (c == null) {
                                  c = new char[endOffset - startOffset];
                                  j = offset - startOffset;
                                  if (j != 0) {
                                      System.arraycopy(ch, startOffset, c, 0, j);
                                  }
                              }
                              int num1 = Character.digit(ch[++offset], 16); //@RWS7
                              int num2 = Character.digit(ch[++offset], 16); //@RWS7
                              if (num1 == -1 || num2 == -1) //@RWS5
                              { //PK75617 starts
                                  if (ignoreInvalidQueryString) {
                                      logger.logp(Level.WARNING, CLASS_NAME, "parseName", "invalid.query.string");
                                      return null;
                                  } //PK75617 ends
                                  throw new IllegalArgumentException(); //@RWS5
                              } //PK75617
                              // c[j++] = (char)(num1*16 + num2);       //@RWS5
                              char newChar = (char) ((num1 << 4) | num2); //@RWS8
                              if (!decodeParamViaReqEncoding && newChar > '\u007F') {
                                  if (isKey) {
                                      isKeySingleByteString = false;
                                  } else {
                                      isValueSingleByteString = false;
                                  }
                              }
                              c[j++] = newChar;
                          } else { // allow '%' at end of value or second to last character (as original code does)
                              if (c != null) {
                                  for (; offset < endOffset; offset++) // @RWS2
                                      c[j++] = ch[offset]; // @RWS7
                              } else {
                                  offset = endOffset;
                              }
                          }
                          break;
                      default:
                          if (c != null) {
                              c[j++] = ch[offset];
                          }
                          break;
                  }
              }
              String returnValue = c != null ? new String(c, 0, j) : new String(ch, startOffset, endOffset - startOffset);
              if (printbyteValueandcharParamdata && com.ibm.ejs.ras.TraceComponent.isAnyTracingEnabled() && logger.isLoggable(Level.FINE))
                  printValues(returnValue, "parseNameOUT");
              return returnValue;
          }
          // @MD17415 end part 3 of 3 New Loop for finding key and value
      }
  
      private static class StringQueryString extends QueryString {
          private final String queryString;
          private final int lgth;
  
          StringQueryString(String queryString) {
              this.queryString = queryString;
              lgth = queryString.length();
          }
  
          protected boolean lessThanEqualLength() {
              return i <= lgth;
          }
  
          protected boolean isEqualLength() {
              return i == lgth;
          }
  
          protected char getNextChar() {
              return queryString.charAt(i);
          }
  
          String getKey() {
              return queryString.substring(pair_start, equalSign);
          }
  
          String getValue() {
              return queryString.substring(equalSign + 1, i);
          }
  
          String parseKey() {
              return parseName(pair_start, equalSign, true);
          }
  
          String parseValue() {
              return parseName(equalSign + 1, i, false);
          }
  
          private String parseName(final int startOffset, final int endOffset, boolean isKey) {
              StringBuilder sb = null;
              for (int offset = startOffset; offset < endOffset; offset++) {
                  char c = queryString.charAt(offset);
                  switch (c) {
                      case '+':
                          if (sb == null) {
                              sb = new StringBuilder(endOffset - startOffset);
                              if (offset != startOffset) {
                                  sb.append(queryString, startOffset, offset);
                              }
                          }
                          sb.append(' ');
                          break;
                      case '%':
                          if (offset + 2 < endOffset) { // @RWS2
                              if (sb == null) {
                                  sb = new StringBuilder(endOffset - startOffset);
                                  if (offset != startOffset) {
                                      sb.append(queryString, startOffset, offset);
                                  }
                              }
                              int num1 = Character.digit(queryString.charAt(++offset), 16); //@RWS7
                              int num2 = Character.digit(queryString.charAt(++offset), 16); //@RWS7
                              if (num1 == -1 || num2 == -1) //@RWS5
                              { //PK75617 starts
                                  if (ignoreInvalidQueryString) {
                                      logger.logp(Level.WARNING, CLASS_NAME, "parseName", "invalid.query.string");
                                      return null;
                                  } //PK75617 ends
                                  throw new IllegalArgumentException(); //@RWS5
                              } //PK75617
                                // c[j++] = (char)(num1*16 + num2);       //@RWS5
                              char newChar = (char) ((num1 << 4) | num2); //@RWS8
                              if (!decodeParamViaReqEncoding && newChar > '\u007F') {
                                  if (isKey) {
                                      isKeySingleByteString = false;
                                  } else {
                                      isValueSingleByteString = false;
                                  }
                              }
                              sb.append(newChar);
                          } else { // allow '%' at end of value or second to last character (as original code does)
                              if (sb != null) {
                                  if (offset < endOffset) {
                                      sb.append(queryString, offset, endOffset);
                                  }
                              } else {
                                  offset = endOffset;
                              }
                          }
                          break;
                      default:
                          if (sb != null) {
                              sb.append(c);
                          }
                          break;
                  }
              }
              String returnValue = sb != null ? sb.toString() : queryString.substring(startOffset, endOffset);
              if (printbyteValueandcharParamdata && com.ibm.ejs.ras.TraceComponent.isAnyTracingEnabled() && logger.isLoggable(Level.FINE))
                  printValues(returnValue, "parseNameOUT");
              return returnValue;
          }
      }
  
      private static class CharArrayQueryString extends QueryString {
          private final char[] queryString;
          private final int lgth;
  
          CharArrayQueryString(char[] queryString) {
              this.queryString = queryString;
              lgth = queryString.length;
          }
  
          protected boolean lessThanEqualLength() {
              return i <= lgth;
          }
  
          protected boolean isEqualLength() {
              return i == lgth;
          }
  
          protected char getNextChar() {
              return queryString[i];
          }
  
          String getKey() {
              return new String(queryString, pair_start, equalSign - pair_start);
          }
  
          String getValue() {
              return new String(queryString, equalSign + 1, i - equalSign - 1);
          }
  
          String parseKey() {
              return parseName(queryString, pair_start, equalSign, true);
          }
  
          String parseValue() {
              return parseName(queryString, equalSign + 1, i, false);
          }
      }
  
      private static class CharArrayArrayQueryString extends QueryString {
  
          private final char[][] queryString;
          private int pair_start_index = 0;
          private int k = 0, equalSign_index = 0;
  
          CharArrayArrayQueryString(char[][] cha) {
              this.queryString = cha;
          }
  
          protected boolean lessThanEqualLength() {
              return k < queryString.length && i <= queryString[k].length;
          }
  
          protected boolean isEqualLength() {
              return k == queryString.length - 1 && i == queryString[queryString.length - 1].length;
          }
  
          protected char getNextChar() {
              return queryString[k][i];
          }
  
          @Override
          protected void increment() {
              i++;
              while (k < queryString.length - 1 && i >= queryString[k].length) {
                  i = 0;
                  k++;
              }
          }
  
          @Override
          protected void setEqualSign() {
              super.setEqualSign();
              equalSign_index = k;
          }
  
          @Override
          boolean findNextPair() {
              while (k < queryString.length && i + 1 >= queryString[k].length) {
                  queryString[k] = null;
                  i = -1;
                  k++;
              }
  
              // release buffers for garbage collection that may not have gotten consumed
              if (pair_start_index < k && queryString[pair_start_index] != null) {
                  for (int j = pair_start_index; j < k; ++j) {
                      queryString[j] = null;
                  }
              }
  
              pair_start_index = k;
  
              return super.findNextPair();
          }
  
          String getKey() {
              char[] nameChars;
              int nameStart;
              int nameLen;
              if (pair_start_index != equalSign_index) {
                  nameChars = getSingleBuffer(queryString, pair_start, pair_start_index, equalSign, equalSign_index);
                  nameStart = 0;
                  nameLen = nameChars.length;
              } else {
                  nameChars = queryString[pair_start_index];
                  nameStart = pair_start;
                  nameLen = equalSign - pair_start;
              }
              return new String(nameChars, nameStart, nameLen);
          }
  
          String getValue() {
              char[] valueChars;
              int valueStart;
              int valueLen;
              if (equalSign_index != k) {
                  valueChars = getSingleBuffer(queryString, equalSign + 1, equalSign_index, i, k);
                  valueStart = 0;
                  valueLen = valueChars.length;
              } else {
                  valueChars = queryString[k];
                  valueStart = equalSign + 1;
                  valueLen = i - valueStart;
              }
              return new String(valueChars, valueStart, valueLen);
          }
  
          String parseKey() {
              char[] nameChars;
              int nameStart;
              int nameEnd;
              if (pair_start_index != equalSign_index) {
                  nameChars = getSingleBuffer(queryString, pair_start, pair_start_index, equalSign, equalSign_index);
                  nameStart = 0;
                  nameEnd = nameChars.length;
              } else {
                  nameChars = queryString[pair_start_index];
                  nameStart = pair_start;
                  nameEnd = equalSign;
              }
              return parseName(nameChars, nameStart, nameEnd, true);
          }
  
          String parseValue() {
              char[] valueChars;
              int valueStart;
              int valueEnd;
              if (equalSign_index != k) {
                  valueChars = getSingleBuffer(queryString, equalSign + 1, equalSign_index, i, k);
                  valueStart = 0;
                  valueEnd = valueChars.length;
              } else {
                  valueChars = queryString[k];
                  valueStart = equalSign + 1;
                  valueEnd = i;
              }
              return parseName(valueChars, valueStart, valueEnd, false);
          }
      }
  
      @SuppressWarnings("rawtypes")
      static private Hashtable parseQueryString(QueryString qs, String encoding) {
          if (com.ibm.ejs.ras.TraceComponent.isAnyTracingEnabled() && logger.isLoggable(Level.FINE)) { //PK75617
              logger.entering(CLASS_NAME, "parseQueryString( QueryString , encoding --> [" + encoding + "])"); //PM35450.1
          } //PK75617
          int totalSize = 0; //PM53930
          int dupSize = 0; // 728397
          Hashtable<String, String[]> ht = new Hashtable<>();
          HashSet<Integer> key_hset = new HashSet<>(); // 728397
          // PK23256 begin
          boolean encoding_is_ShortEnglish = isShortEnglishEncoding(encoding);
          // PK23256 end
  
          while (qs.findNextPair()) {
  
              //logger.logp(Level.FINE, CLASS_NAME,"parseQueryString", "equalSign =" + equalSign);
  
              if (qs.hasEquals() || allowQueryParamWithNoEqual) //PM35450 , parameter is blah and not blah=
              { // equal sign found at offset equalSign
                  String key = null; //PM92940 Start
                  String value = null;
  
                  if (decodeParamViaReqEncoding && (!encoding_is_ShortEnglish)) {
  
                      // data, start, (end-start),encoding, string
                      key = parse_decode_Parameter(qs.getKey(), encoding, "paramKey");
  
                      if (!qs.hasEquals()) {
                          value = key != null ? EMPTY_STRING : null;
                      } else {
                          value = parse_decode_Parameter(qs.getValue(), encoding, "paramValue");
                      }
  
                      if (ignoreInvalidQueryString && ((value == null) || (key == null))) {
                          continue;
                      }
  
                  } else {
                      key = qs.parseKey();
                      //PM35450 Start
                      //String value = null;
                      if (!qs.hasEquals()) {
                          value = key != null ? EMPTY_STRING : null;
                      } else {
                          value = qs.parseValue();
                      }
                      //PM35450 End
  
                      if (ignoreInvalidQueryString && ((value == null) || (key == null))) { //PK75617
                          continue;
                      } //PK75617
                      if (!encoding_is_ShortEnglish) {
                          try {
                              if (!qs.isKeySingleByteString()) {
                                  key = new String(key.getBytes(SHORT_ENGLISH), encoding);
                              }
                              if (!qs.isValueSingleByteString()) {
                                  value = new String(value.getBytes(SHORT_ENGLISH), encoding);
                              }
                          } catch (UnsupportedEncodingException uee) {
                              //No need to nls. SHORT_ENGLISH will always be supported
                              logger.logp(Level.SEVERE, CLASS_NAME, "parseQueryString", "unsupported exception", uee);
  