HttpURL.java
  1  /*
  2   * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/HttpURL.java,v 1.18 2004/09/30 17:26:41 oglueck Exp $
  3   * $Revision: 507324 $
  4   * $Date: 2007-02-14 01:12:11 +0100 (Wed, 14 Feb 2007) $
  5   *
  6   * ====================================================================
  7   *
  8   *  Licensed to the Apache Software Foundation (ASF) under one or more
  9   *  contributor license agreements.  See the NOTICE file distributed with
 10   *  this work for additional information regarding copyright ownership.
 11   *  The ASF licenses this file to You under the Apache License, Version 2.0
 12   *  (the "License"); you may not use this file except in compliance with
 13   *  the License.  You may obtain a copy of the License at
 14   *
 15   *      http://www.apache.org/licenses/LICENSE-2.0
 16   *
 17   *  Unless required by applicable law or agreed to in writing, software
 18   *  distributed under the License is distributed on an "AS IS" BASIS,
 19   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 20   *  See the License for the specific language governing permissions and
 21   *  limitations under the License.
 22   * ====================================================================
 23   *
 24   * This software consists of voluntary contributions made by many
 25   * individuals on behalf of the Apache Software Foundation.  For more
 26   * information on the Apache Software Foundation, please see
 27   * <http://www.apache.org/>.
 28   *
 29   */
 30  
 31  package org.apache.commons.httpclient;
 32  
 33  import org.apache.commons.httpclient.util.URIUtil;
 34  
 35  /**
 36   * The HTTP URL.
 37   *
 38   * @author <a href="mailto:jericho at apache.org">Sung-Gu</a>
 39   * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
 40   */
 41  public class HttpURL extends URI {
 42  
 43      // ----------------------------------------------------------- Constructors
 44  
 45      /** Create an instance as an internal use. */
 46      protected HttpURL() {
 47      }
 48  
 49  
 50      /**
 51       * Construct a HTTP URL as an escaped form of a character array with the
 52       * given charset to do escape encoding.
 53       *
 54       * @param escaped the HTTP URL character sequence
 55       * @param charset the charset string to do escape encoding
 56       * @throws URIException If {@link #checkValid()} fails
 57       * @throws NullPointerException if <code>escaped</code> is <code>null</code>
 58       * @see #getProtocolCharset
 59       */
 60      public HttpURL(char[] escaped, String charset)
 61          throws URIException, NullPointerException {
 62          protocolCharset = charset;
 63          parseUriReference(new String(escaped), true);
 64          checkValid();
 65      }
 66  
 67  
 68      /**
 69       * Construct a HTTP URL as an escaped form of a character array.
 70       *
 71       * @param escaped the HTTP URL character sequence
 72       * @throws URIException If {@link #checkValid()} fails
 73       * @throws NullPointerException if <code>escaped</code> is <code>null</code>
 74       * @see #getDefaultProtocolCharset
 75       */
 76      public HttpURL(char[] escaped) throws URIException, NullPointerException {
 77          parseUriReference(new String(escaped), true);
 78          checkValid();
 79      }
 80  
 81  
 82      /**
 83       * Construct a HTTP URL from a given string with the given charset to do
 84       * escape encoding.
 85       *
 86       * @param original the HTTP URL string
 87       * @param charset the charset string to do escape encoding
 88       * @throws URIException If {@link #checkValid()} fails
 89       * @see #getProtocolCharset
 90       */
 91      public HttpURL(String original, String charset) throws URIException {
 92          protocolCharset = charset;
 93          parseUriReference(original, false);
 94          checkValid();
 95      }
 96  
 97  
 98      /**
 99       * Construct a HTTP URL from a given string.
100       *
101       * @param original the HTTP URL string
102       * @throws URIException If {@link #checkValid()} fails
103       * @see #getDefaultProtocolCharset
104       */
105      public HttpURL(String original) throws URIException {
106          parseUriReference(original, false);
107          checkValid();
108      }
109  
110  
111      /**
112       * Construct a HTTP URL from given components.
113       *
114       * @param host the host string
115       * @param port the port number
116       * @param path the path string
117       * @throws URIException If {@link #checkValid()} fails
118       * @see #getDefaultProtocolCharset
119       */
120      public HttpURL(String host, int port, String path) throws URIException {
121          this(null, null, host, port, path, null, null);
122      }
123  
124  
125      /**
126       * Construct a HTTP URL from given components.
127       *
128       * @param host the host string
129       * @param port the port number
130       * @param path the path string
131       * @param query the query string
132       * @throws URIException If {@link #checkValid()} fails
133       * @see #getDefaultProtocolCharset
134       */
135      public HttpURL(String host, int port, String path, String query)
136          throws URIException {
137  
138          this(null, null, host, port, path, query, null);
139      }
140  
141  
142      /**
143       * Construct a HTTP URL from given components.
144       *
145       * @param user the user name
146       * @param password his or her password
147       * @param host the host string
148       * @throws URIException If {@link #checkValid()} fails
149       * @see #getDefaultProtocolCharset
150       */
151      public HttpURL(String user, String password, String host)
152          throws URIException {
153  
154          this(user, password, host, -1, null, null, null);
155      }
156  
157  
158      /**
159       * Construct a HTTP URL from given components.
160       *
161       * @param user the user name
162       * @param password his or her password
163       * @param host the host string
164       * @param port the port number
165       * @throws URIException If {@link #checkValid()} fails
166       * @see #getDefaultProtocolCharset
167       */
168      public HttpURL(String user, String password, String host, int port)
169          throws URIException {
170  
171          this(user, password, host, port, null, null, null);
172      }
173  
174  
175      /**
176       * Construct a HTTP URL from given components.
177       *
178       * @param user the user name
179       * @param password his or her password
180       * @param host the host string
181       * @param port the port number
182       * @param path the path string
183       * @throws URIException If {@link #checkValid()} fails
184       * @see #getDefaultProtocolCharset
185       */
186      public HttpURL(String user, String password, String host, int port,
187              String path) throws URIException {
188  
189          this(user, password, host, port, path, null, null);
190      }
191  
192  
193      /**
194       * Construct a HTTP URL from given components.
195       *
196       * @param user the user name
197       * @param password his or her password
198       * @param host the host string
199       * @param port the port number
200       * @param path the path string
201       * @param query The query string.
202       * @throws URIException If {@link #checkValid()} fails
203       * @see #getDefaultProtocolCharset
204       */
205      public HttpURL(String user, String password, String host, int port,
206              String path, String query) throws URIException {
207  
208          this(user, password, host, port, path, query, null);
209      }
210  
211  
212      /**
213       * Construct a HTTP URL from given components.
214       *
215       * @param host the host string
216       * @param path the path string
217       * @param query the query string
218       * @param fragment the fragment string
219       * @throws URIException If {@link #checkValid()} fails
220       * @see #getDefaultProtocolCharset
221       */
222      public HttpURL(String host, String path, String query, String fragment)
223          throws URIException {
224  
225          this(null, null, host, -1, path, query, fragment);
226      }
227  
228  
229      /**
230       * Construct a HTTP URL from given components.
231       * 
232       * Note: The <code>userinfo</code> format is normally
233       * <code>&lt;username&gt;:&lt;password&gt;</code> where
234       * username and password must both be URL escaped. 
235       *
236       * @param userinfo the userinfo string whose parts are URL escaped
237       * @param host the host string
238       * @param path the path string
239       * @param query the query string
240       * @param fragment the fragment string
241       * @throws URIException If {@link #checkValid()} fails
242       * @see #getDefaultProtocolCharset
243       */
244      public HttpURL(String userinfo, String host, String path, String query,
245              String fragment) throws URIException {
246  
247          this(userinfo, host, -1, path, query, fragment);
248      }
249  
250  
251      /**
252       * Construct a HTTP URL from given components.
253       *
254       * Note: The <code>userinfo</code> format is normally
255       * <code>&lt;username&gt;:&lt;password&gt;</code> where
256       * username and password must both be URL escaped.
257       *  
258       * @param userinfo the userinfo string whose parts are URL escaped
259       * @param host the host string
260       * @param port the port number
261       * @param path the path string
262       * @throws URIException If {@link #checkValid()} fails
263       * @see #getDefaultProtocolCharset
264       */
265      public HttpURL(String userinfo, String host, int port, String path)
266          throws URIException {
267  
268          this(userinfo, host, port, path, null, null);
269      }
270  
271  
272      /**
273       * Construct a HTTP URL from given components.
274       *
275       * Note: The <code>userinfo</code> format is normally
276       * <code>&lt;username&gt;:&lt;password&gt;</code> where
277       * username and password must both be URL escaped.
278       *  
279       * @param userinfo the userinfo string whose parts are URL escaped
280       * @param host the host string
281       * @param port the port number
282       * @param path the path string
283       * @param query the query string
284       * @throws URIException If {@link #checkValid()} fails
285       * @see #getDefaultProtocolCharset
286       */
287      public HttpURL(String userinfo, String host, int port, String path,
288              String query) throws URIException {
289  
290          this(userinfo, host, port, path, query, null);
291      }
292  
293  
294      /**
295       * Construct a HTTP URL from given components.
296       *
297       * Note: The <code>userinfo</code> format is normally
298       * <code>&lt;username&gt;:&lt;password&gt;</code> where
299       * username and password must both be URL escaped.
300       *  
301       * @param userinfo the userinfo string whose parts are URL escaped
302       * @param host the host string
303       * @param port the port number
304       * @param path the path string
305       * @param query the query string
306       * @param fragment the fragment string
307       * @throws URIException If {@link #checkValid()} fails
308       * @see #getDefaultProtocolCharset
309       */
310      public HttpURL(String userinfo, String host, int port, String path,
311              String query, String fragment) throws URIException {
312  
313          // validate and contruct the URI character sequence
314          StringBuffer buff = new StringBuffer();
315          if (userinfo != null || host != null || port != -1) {
316              _scheme = DEFAULT_SCHEME; // in order to verify the own protocol
317              buff.append(_default_scheme);
318              buff.append("://");
319              if (userinfo != null) {
320                  buff.append(userinfo);
321                  buff.append('@');
322              }
323              if (host != null) {
324                  buff.append(URIUtil.encode(host, URI.allowed_host));
325                  if (port != -1 || port != DEFAULT_PORT) {
326                      buff.append(':');
327                      buff.append(port);
328                  }
329              }
330          }
331          if (path != null) {  // accept empty path
332              if (scheme != null && !path.startsWith("/")) {
333                  throw new URIException(URIException.PARSING,
334                          "abs_path requested");
335              }
336              buff.append(URIUtil.encode(path, URI.allowed_abs_path));
337          }
338          if (query != null) {
339              buff.append('?');
340              buff.append(URIUtil.encode(query, URI.allowed_query));
341          }
342          if (fragment != null) {
343              buff.append('#');
344              buff.append(URIUtil.encode(fragment, URI.allowed_fragment));
345          }
346          parseUriReference(buff.toString(), true);
347          checkValid();
348      }
349  
350  
351      /**
352       * Construct a HTTP URL from given components.
353       *
354       * @param user the user name
355       * @param password his or her password
356       * @param host the host string
357       * @param port the port number
358       * @param path the path string
359       * @param query the query string
360       * @param fragment the fragment string
361       * @throws URIException If {@link #checkValid()} fails
362       * @see #getDefaultProtocolCharset
363       */
364      public HttpURL(String user, String password, String host, int port,
365              String path, String query, String fragment) throws URIException {
366          this(toUserinfo(user, password), host, port, path, query, fragment);
367      }
368      
369      protected static String toUserinfo(String user, String password) throws URIException {
370          if (user == null) return null;
371          StringBuffer usrinfo = new StringBuffer(20); //sufficient for real world
372          usrinfo.append(URIUtil.encode(user, URI.allowed_within_userinfo));
373          if (password == null) return usrinfo.toString();
374          usrinfo.append(':');
375          usrinfo.append(URIUtil.encode(password, URI.allowed_within_userinfo));
376          return usrinfo.toString();
377      }
378  
379  
380      /**
381       * Construct a HTTP URL with a given relative URL string.
382       *
383       * @param base the base HttpURL
384       * @param relative the relative HTTP URL string
385       * @throws URIException If {@link #checkValid()} fails
386       */
387      public HttpURL(HttpURL base, String relative) throws URIException {
388          this(base, new HttpURL(relative));
389      }
390  
391  
392      /**
393       * Construct a HTTP URL with a given relative URL.
394       *
395       * @param base the base HttpURL
396       * @param relative the relative HttpURL
397       * @throws URIException If {@link #checkValid()} fails
398       */
399      public HttpURL(HttpURL base, HttpURL relative) throws URIException {
400          super(base, relative);
401          checkValid();
402      }
403  
404      // -------------------------------------------------------------- Constants
405  
406      /**
407       * Default scheme for HTTP URL.
408       */
409      public static final char[] DEFAULT_SCHEME = { 'h', 't', 't', 'p' };
410  
411      /**
412       * Default scheme for HTTP URL.
413       * @deprecated Use {@link #DEFAULT_SCHEME} instead.  This one doesn't
414       * conform to the project naming conventions.
415       */
416      public static final char[] _default_scheme = DEFAULT_SCHEME;
417  
418      /**
419       * Default port for HTTP URL.
420       */
421      public static final int DEFAULT_PORT = 80;
422  
423      /**
424       * Default port for HTTP URL.
425       * @deprecated Use {@link #DEFAULT_PORT} instead.  This one doesn't conform
426       * to the project naming conventions.
427       */
428      public static final int _default_port = DEFAULT_PORT;
429  
430      /**
431       * The serialVersionUID.
432       */
433      static final long serialVersionUID = -7158031098595039459L;
434  
435      // ------------------------------------------------------------- The scheme
436  
437      /**
438       * Get the scheme.  You can get the scheme explicitly.
439       *
440       * @return the scheme
441       */
442      public char[] getRawScheme() {
443          return (_scheme == null) ? null : HttpURL.DEFAULT_SCHEME;
444      }
445  
446  
447      /**
448       * Get the scheme.  You can get the scheme explicitly.
449       *
450       * @return the scheme null if empty or undefined
451       */
452      public String getScheme() {
453          return (_scheme == null) ? null : new String(HttpURL.DEFAULT_SCHEME);
454      }
455  
456      // --------------------------------------------------------------- The port
457  
458      /**
459       * Get the port number.
460       * @return the port number
461       */
462      public int getPort() {
463          return (_port == -1) ? HttpURL.DEFAULT_PORT : _port;
464      }
465  
466      // ----------------------------------------------------------- The userinfo
467  
468      /**
469       * Set the raw-escaped user and password.
470       *
471       * @param escapedUser the raw-escaped user
472       * @param escapedPassword the raw-escaped password; could be null
473       * @throws URIException escaped user not valid or user required; escaped
474       * password not valid or username missed
475       */
476      public void setRawUserinfo(char[] escapedUser, char[] escapedPassword)
477          throws URIException {
478  
479          if (escapedUser == null || escapedUser.length == 0) {
480              throw new URIException(URIException.PARSING, "user required");
481          }
482          if (!validate(escapedUser, within_userinfo) 
483              || ((escapedPassword != null) 
484              && !validate(escapedPassword, within_userinfo))) {
485              throw new URIException(URIException.ESCAPING,
486                      "escaped userinfo not valid");
487          }
488          String username = new String(escapedUser);
489          String password = (escapedPassword == null) 
490              ? null : new String(escapedPassword);
491          String userinfo = username + ((password == null) ? "" : ":" + password);
492          String hostname = new String(getRawHost());
493          String hostport = (_port == -1) ? hostname : hostname + ":" + _port;
494          String authority = userinfo + "@" + hostport;
495          _userinfo = userinfo.toCharArray();
496          _authority = authority.toCharArray();
497          setURI();
498      }
499  
500  
501      /**
502       * Set the raw-escaped user and password.
503       *
504       * @param escapedUser the escaped user
505       * @param escapedPassword the escaped password; could be null
506       * @throws URIException escaped user not valid or user required; escaped
507       * password not valid or username missed
508       * @throws NullPointerException null user
509       */
510      public void setEscapedUserinfo(String escapedUser, String escapedPassword)
511          throws URIException, NullPointerException {
512  
513          setRawUserinfo(escapedUser.toCharArray(), (escapedPassword == null) 
514              ? null : escapedPassword.toCharArray());
515      }
516  
517  
518      /**
519       * Set the user and password.
520       *
521       * @param user the user
522       * @param password the password; could be null
523       * @throws URIException encoding error or username missed
524       * @throws NullPointerException null user
525       */
526      public void setUserinfo(String user, String password) 
527          throws URIException, NullPointerException {
528          // set the charset to do escape encoding
529          String charset = getProtocolCharset();
530          setRawUserinfo(encode(user, within_userinfo, charset),
531                  (password == null) 
532                  ? null 
533                  : encode(password, within_userinfo, charset));
534      }
535  
536  
537      /**
538       * Set the raw-escaped user.
539       *
540       * @param escapedUser the raw-escaped user
541       * @throws URIException escaped user not valid or user required
542       */
543      public void setRawUser(char[] escapedUser) throws URIException {
544          if (escapedUser == null || escapedUser.length == 0) {
545              throw new URIException(URIException.PARSING, "user required");
546          }
547          if (!validate(escapedUser, within_userinfo)) {
548              throw new URIException(URIException.ESCAPING,
549                      "escaped user not valid");
550          }
551          String username = new String(escapedUser);
552          char[] rawPassword = getRawPassword();
553          String password = rawPassword == null ? null : new String(rawPassword);
554          String userinfo = username + ((password == null) ? "" : ":" + password);
555          String hostname = new String(getRawHost());
556          String hostport = (_port == -1) ? hostname : hostname + ":" + _port;
557          String authority = userinfo + "@" + hostport;
558          _userinfo = userinfo.toCharArray();
559          _authority = authority.toCharArray();
560          setURI();
561      }
562  
563  
564      /**
565       * Set the escaped user string.
566       *
567       * @param escapedUser the escaped user string
568       * @throws URIException escaped user not valid
569       * @throws NullPointerException null user
570       */
571      public void setEscapedUser(String escapedUser)
572          throws URIException, NullPointerException {
573          setRawUser(escapedUser.toCharArray());
574      }
575  
576  
577      /**
578       * Set the user string.
579       *
580       * @param user the user string
581       * @throws URIException user encoding error
582       * @throws NullPointerException null user
583       */
584      public void setUser(String user) throws URIException, NullPointerException {
585          setRawUser(encode(user, allowed_within_userinfo, getProtocolCharset()));
586      }
587  
588  
589      /**
590       * Get the raw-escaped user.
591       *
592       * @return the raw-escaped user
593       */
594      public char[] getRawUser() {
595          if (_userinfo == null || _userinfo.length == 0) {
596              return null;
597          }
598          int to = indexFirstOf(_userinfo, ':');
599          // String.indexOf(':', 0, _userinfo.length, _userinfo, 0, 1, 0);
600          if (to == -1) {
601              return _userinfo; // only user.
602          }
603          char[] result = new char[to];
604          System.arraycopy(_userinfo, 0, result, 0, to);
605          return result;
606      }
607  
608  
609      /**
610       * Get the escaped user
611       *
612       * @return the escaped user
613       */
614      public String getEscapedUser() {
615          char[] user = getRawUser();
616          return (user == null) ? null : new String(user);
617      }
618  
619  
620      /**
621       * Get the user.
622       *
623       * @return the user name
624       * @throws URIException If {@link #decode} fails
625       */
626      public String getUser() throws URIException {
627          char[] user = getRawUser();
628          return (user == null) ? null : decode(user, getProtocolCharset());
629      }
630  
631  
632      /**
633       * Set the raw-escaped password.
634       *
635       * @param escapedPassword the raw-escaped password; could be null
636       * @throws URIException escaped password not valid or username missed
637       */
638      public void setRawPassword(char[] escapedPassword) throws URIException {
639          if (escapedPassword != null 
640              && !validate(escapedPassword, within_userinfo)) {
641              throw new URIException(URIException.ESCAPING,
642                 "escaped password not valid");
643          }
644          if (getRawUser() == null || getRawUser().length == 0) {
645              throw new URIException(URIException.PARSING, "username required");
646          }
647          String username = new String(getRawUser());
648          String password = escapedPassword == null ? null : new String(escapedPassword);
649          // an emtpy string is allowed as a password
650          String userinfo = username + ((password == null) ? "" : ":" + password);
651          String hostname = new String(getRawHost());
652          String hostport = (_port == -1) ? hostname : hostname + ":" + _port;
653          String authority = userinfo + "@" + hostport;
654          _userinfo = userinfo.toCharArray();
655          _authority = authority.toCharArray();
656          setURI();
657      }
658  
659  
660      /**
661       * Set the escaped password string.
662       *
663       * @param escapedPassword the escaped password string; could be null
664       * @throws URIException escaped password not valid or username missed
665       */
666      public void setEscapedPassword(String escapedPassword) throws URIException {
667          setRawPassword((escapedPassword == null) ? null 
668              : escapedPassword.toCharArray());
669      }
670  
671  
672      /**
673       * Set the password string.
674       *
675       * @param password the password string; could be null
676       * @throws URIException encoding error or username missed
677       */
678      public void setPassword(String password) throws URIException {
679          setRawPassword((password == null) ? null : encode(password,
680                      allowed_within_userinfo, getProtocolCharset()));
681      }
682  
683  
684      /**
685       * Get the raw-escaped password.
686       *
687       * @return the raw-escaped password
688       */
689      public char[] getRawPassword() {
690          int from = indexFirstOf(_userinfo, ':');
691          if (from == -1) {
692              return null; // null or only user.
693          }
694          int len = _userinfo.length - from - 1;
695          char[] result = new char[len];
696          System.arraycopy(_userinfo, from + 1, result, 0, len);
697          return result;
698      }
699  
700  
701      /**
702       * Get the escaped password.
703       *
704       * @return the escaped password
705       */
706      public String getEscapedPassword() {
707          char[] password = getRawPassword();
708          return (password == null) ? null : new String(password);
709      }
710  
711  
712      /**
713       * Get the password.
714       *
715       * @return the password
716       * @throws URIException If {@link #decode(char[],String)} fails.
717       */
718      public String getPassword() throws URIException {
719          char[] password = getRawPassword();
720          return (password == null) ? null : decode(password,
721                  getProtocolCharset());
722      }
723  
724      // --------------------------------------------------------------- The path
725  
726      /**
727       * Get the raw-escaped current hierarchy level.
728       *
729       * @return the raw-escaped current hierarchy level
730       * @throws URIException If {@link #getRawCurrentHierPath(char[])} fails.
731       */
732      public char[] getRawCurrentHierPath() throws URIException {
733          return (_path == null || _path.length == 0) ? rootPath 
734              : super.getRawCurrentHierPath(_path);
735      }
736  
737  
738      /**
739       * Get the level above the this hierarchy level.
740       *
741       * @return the raw above hierarchy level
742       * @throws URIException If {@link #getRawCurrentHierPath(char[])} fails.
743       */
744      public char[] getRawAboveHierPath() throws URIException {
745          char[] path = getRawCurrentHierPath();
746          return (path == null || path.length == 0) ? rootPath : getRawCurrentHierPath(path);
747      }
748  
749  
750      /**
751       * Get the raw escaped path.
752       *
753       * @return the path '/' if empty or undefined
754       */
755      public char[] getRawPath() {
756          char[] path =  super.getRawPath();
757          return (path == null || path.length == 0) ? rootPath : path;
758      }
759  
760      // -------------------------------------------------------------- The query
761  
762      /**
763       * Set the query as the name and value pair.
764       *
765       * @param queryName the query string.
766       * @param queryValue the query string.
767       * @throws URIException incomplete trailing escape pattern
768       * Or unsupported character encoding
769       * @throws NullPointerException null query
770       * @see #encode
771       */
772      public void setQuery(String queryName, String queryValue)
773          throws URIException, NullPointerException {
774  
775          StringBuffer buff = new StringBuffer();
776          // set the charset to do escape encoding
777          String charset = getProtocolCharset();
778          buff.append(encode(queryName, allowed_within_query, charset));
779          buff.append('=');
780          buff.append(encode(queryValue, allowed_within_query, charset));
781          _query = buff.toString().toCharArray();
782          setURI();
783      }
784  
785  
786      /**
787       * Set the query as the name and value pairs.
788       *
789       * @param queryName the array of the query string.
790       * @param queryValue the array of the query string.
791       * @throws URIException incomplete trailing escape pattern,
792       * unsupported character encoding or wrong array size
793       * @throws NullPointerException null query
794       * @see #encode
795       */
796      public void setQuery(String[] queryName, String[] queryValue)
797          throws URIException, NullPointerException {
798  
799          int length = queryName.length;
800          if (length != queryValue.length) {
801              throw new URIException("wrong array size of query");
802          }
803  
804          StringBuffer buff = new StringBuffer();
805          // set the charset to do escape encoding
806          String charset = getProtocolCharset();
807          for (int i = 0; i < length; i++) {
808              buff.append(encode(queryName[i], allowed_within_query, charset));
809              buff.append('=');
810              buff.append(encode(queryValue[i], allowed_within_query, charset));
811              if (i + 1 < length) { 
812                  buff.append('&');
813              }
814          }
815          _query = buff.toString().toCharArray();
816          setURI();
817      }
818  
819      // ---------------------------------------------------------------- Utility
820  
821      /**
822       * Verify the valid class use for construction.
823       *
824       * @throws URIException the wrong scheme use
825       */
826      protected void checkValid() throws URIException {
827          // could be explicit protocol or undefined.
828          if (!(equals(_scheme, DEFAULT_SCHEME) || _scheme == null)) {
829              throw new URIException(URIException.PARSING, "wrong class use");
830          }
831      }
832  
833      /**
834       * Once it's parsed successfully, set this URI.
835       *
836       * @see #getRawURI
837       */
838      protected void setURI() {
839          // set _uri
840          StringBuffer buf = new StringBuffer();
841          // ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
842          if (_scheme != null) {
843              buf.append(_scheme);
844              buf.append(':');
845          }
846          if (_is_net_path) {
847              buf.append("//");
848              if (_authority != null) { // has_authority
849                  if (_userinfo != null) { // by default, remove userinfo part
850                      if (_host != null) {
851                          buf.append(_host);
852                          if (_port != -1) {
853                              buf.append(':');
854                              buf.append(_port);
855                          }
856                      }
857                  } else {
858                      buf.append(_authority);
859                  }
860              }
861          }
862          if (_opaque != null && _is_opaque_part) {
863              buf.append(_opaque);
864          } else if (_path != null) {
865              // _is_hier_part or _is_relativeURI
866              if (_path.length != 0) {
867                  buf.append(_path);
868              }
869          }
870          if (_query != null) { // has_query
871              buf.append('?');
872              buf.append(_query);
873          }
874          // ignore the fragment identifier
875          _uri = buf.toString().toCharArray();
876          hash = 0;
877      }
878      
879  }
880