Coverage Report - wjhk.jupload2.upload.helper.ByteArrayEncoderHTTP
 
Classes in this File Line Coverage Branch Coverage Complexity
ByteArrayEncoderHTTP
44 %
63/141
13 %
5/36
2,947
 
 1  
 //
 2  
 // $Id: FileUploadThreadFTP.java 136 2007-05-12 20:15:36 +0000 (sam., 12 mai
 3  
 // 2007) etienne_sf $
 4  
 //
 5  
 // jupload - A file upload applet.
 6  
 // Copyright 2007 The JUpload Team
 7  
 //
 8  
 // Created: 2007-12-11
 9  
 // Creator: etienne_sf
 10  
 // Last modified: $Date: 2007-07-21 09:42:51 +0200 (sam., 21 juil. 2007) $
 11  
 //
 12  
 // This program is free software; you can redistribute it and/or modify it under
 13  
 // the terms of the GNU General Public License as published by the Free Software
 14  
 // Foundation; either version 2 of the License, or (at your option) any later
 15  
 // version. This program is distributed in the hope that it will be useful, but
 16  
 // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 17  
 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 18  
 // details. You should have received a copy of the GNU General Public License
 19  
 // along with this program; if not, write to the Free Software Foundation, Inc.,
 20  
 // 675 Mass Ave, Cambridge, MA 02139, USA.
 21  
 
 22  
 package wjhk.jupload2.upload.helper;
 23  
 
 24  
 import java.io.ByteArrayOutputStream;
 25  
 import java.io.IOException;
 26  
 import java.io.OutputStreamWriter;
 27  
 import java.io.UnsupportedEncodingException;
 28  
 import java.io.Writer;
 29  
 
 30  
 import wjhk.jupload2.exception.JUploadIOException;
 31  
 import wjhk.jupload2.policies.UploadPolicy;
 32  
 
 33  
 /**
 34  
  * This class is a utility, which provide easy encoding for HTTP queries. The
 35  
  * way to use this class is:
 36  
  * <OL TYPE=1>
 37  
  * <LI>Instantiate a new object
 38  
  * <LI>Append data to it, using the append methods. Available for: String,
 39  
  * byte[], other ByteArrayEncode...
 40  
  * <LI>Close the stream. This will prevent any new data to be appended to it.
 41  
  * The encoded length can now be calculated.
 42  
  * <LI>Get the encoded length.
 43  
  * <LI>Get the encoded byte array
 44  
  * </OL>
 45  
  * 
 46  
  * @author etienne_sf
 47  
  * 
 48  
  */
 49  
 
 50  
 public class ByteArrayEncoderHTTP implements ByteArrayEncoder {
 51  
 
 52  
     /**
 53  
      * The default encoding. It can be retrieved with
 54  
      * {@link #getDefaultEncoding()}.
 55  
      */
 56  
     final static String DEFAULT_ENCODING = "UTF-8";
 57  
 
 58  
     /**
 59  
      * The boundary, to put between to post variables. Can not be changed during
 60  
      * the object 'life'.
 61  
      */
 62  22
     private String bound = null;
 63  
 
 64  
     /**
 65  
      * The current encoding. Can not be changed during the object 'life'.
 66  
      */
 67  22
     private String encoding = DEFAULT_ENCODING;
 68  
 
 69  
     /**
 70  
      * Indicate whether the encoder is closed or not. If closed, it's impossible
 71  
      * to append new data to it. If not closed, it's impossible to get the
 72  
      * encoded length or the encoded byte array.<BR>
 73  
      * <B>Note:</B> a closed byte array can not be re-opened.
 74  
      */
 75  22
     boolean closed = false;
 76  
 
 77  
     /**
 78  
      * The actual array, which will collect the encoded bytes.
 79  
      */
 80  22
     private ByteArrayOutputStream baos = new ByteArrayOutputStream();
 81  
 
 82  
     /**
 83  
      * The byte array length. Calculated when the ByteArrayOutput is closed.
 84  
      */
 85  22
     private int encodedLength = -1;
 86  
 
 87  
     /**
 88  
      * The encoded byte array. Calculated when the ByteArrayOutput is closed.
 89  
      */
 90  22
     private byte[] encodedByteArray = null;
 91  
 
 92  
     /**
 93  
      * The current upload policy.
 94  
      */
 95  
     private UploadPolicy uploadPolicy;
 96  
 
 97  
     /**
 98  
      * The writer, that will encode the input parameters to {@link #baos}.
 99  
      */
 100  
     private Writer writer;
 101  
 
 102  
     // ///////////////////////////////////////////////////////////////////////
 103  
     // //////////////// VARIOUS UTILITIES ////////////////////////////////////
 104  
     // ///////////////////////////////////////////////////////////////////////
 105  
 
 106  
     // ///////////////////////////////////////////////////////////////////////
 107  
     // //////////////// CONSTRUCTORS /////////////////////////////////////////
 108  
     // ///////////////////////////////////////////////////////////////////////
 109  
 
 110  
     /**
 111  
      * Create an encoder, using the DEFAULT_ENCODING encoding.
 112  
      * 
 113  
      * @param uploadPolicy The current upload policy
 114  
      * @throws JUploadIOException Any IO exception
 115  
      */
 116  
     public ByteArrayEncoderHTTP(UploadPolicy uploadPolicy)
 117  4
             throws JUploadIOException {
 118  4
         init(uploadPolicy, null, DEFAULT_ENCODING);
 119  4
     }
 120  
 
 121  
     /**
 122  
      * Create an encoder, and specifies the encoding to use.
 123  
      * 
 124  
      * @param uploadPolicy The current upload policy
 125  
      * @param bound Any specific boundary. May be null: in this case a default
 126  
      *            boundary is used.
 127  
      * @throws JUploadIOException Any IO exception
 128  
      */
 129  
     public ByteArrayEncoderHTTP(UploadPolicy uploadPolicy, String bound)
 130  2
             throws JUploadIOException {
 131  2
         init(uploadPolicy, bound, DEFAULT_ENCODING);
 132  2
     }
 133  
 
 134  
     /**
 135  
      * Create an encoder, and specifies the boundary and encoding to use.
 136  
      * 
 137  
      * @param uploadPolicy The current upload policy
 138  
      * @param bound Any specific boundary. May be null: in this case a default
 139  
      *            boundary is used.
 140  
      * @param encoding The encoding to use. For instance, "UTF-8".
 141  
      * @throws JUploadIOException Any IO exception
 142  
      */
 143  
     public ByteArrayEncoderHTTP(UploadPolicy uploadPolicy, String bound,
 144  16
             String encoding) throws JUploadIOException {
 145  16
         init(uploadPolicy, bound, encoding);
 146  16
     }
 147  
 
 148  
     // ///////////////////////////////////////////////////////////////////////
 149  
     // //////////////// Public methods ///////////////////////////////////////
 150  
     // ///////////////////////////////////////////////////////////////////////
 151  
 
 152  
     /**
 153  
      * @throws JUploadIOException
 154  
      * @see wjhk.jupload2.upload.helper.ByteArrayEncoder#close()
 155  
      */
 156  
     synchronized public void close() throws JUploadIOException {
 157  12
         if (isClosed()) {
 158  0
             throw new JUploadIOException(
 159  
                     "Trying to close an already closed ByteArrayEncoded");
 160  
         }
 161  
         try {
 162  12
             this.writer.close();
 163  0
         } catch (IOException e) {
 164  0
             throw new JUploadIOException(e);
 165  12
         }
 166  12
         this.encodedByteArray = this.baos.toByteArray();
 167  12
         this.encodedLength = this.encodedByteArray.length;
 168  12
         this.closed = true;
 169  12
     }
 170  
 
 171  
     /** {@inheritDoc} */
 172  
     public ByteArrayEncoder append(String str) throws JUploadIOException {
 173  
         try {
 174  19
             this.writer.append(str);
 175  0
         } catch (IOException e) {
 176  0
             throw new JUploadIOException(e);
 177  19
         }
 178  
         // Returning the encoder allows calls like:
 179  
         // bae.append("qdqd").append("qsldqd"); (like StringBuffer)
 180  19
         return this;
 181  
     }
 182  
 
 183  
     /** {@inheritDoc} */
 184  
     public ByteArrayEncoder append(int b) throws JUploadIOException {
 185  
         try {
 186  1
             this.writer.flush();
 187  1
             this.baos.write(b);
 188  0
         } catch (IOException e) {
 189  0
             throw new JUploadIOException(e);
 190  1
         }
 191  
         // Returning the encoder allows calls like:
 192  
         // bae.append("qdqd").append("qsldqd"); (like StringBuffer)
 193  1
         return this;
 194  
     }
 195  
 
 196  
     /** {@inheritDoc} */
 197  
     public ByteArrayEncoder append(byte[] b) throws JUploadIOException {
 198  
         try {
 199  2
             this.writer.flush();
 200  2
             this.baos.write(b);
 201  0
         } catch (IOException e) {
 202  0
             throw new JUploadIOException(e);
 203  2
         }
 204  
         // Returning the encoder allows calls like:
 205  
         // bae.append("qdqd").append("qsldqd"); (like StringBuffer)
 206  2
         return this;
 207  
     }
 208  
 
 209  
     /** {@inheritDoc} */
 210  
     public ByteArrayEncoder append(ByteArrayEncoder bae)
 211  
             throws JUploadIOException {
 212  1
         this.append(bae.getEncodedByteArray());
 213  
 
 214  1
         return this;
 215  
     }
 216  
 
 217  
     /** {@inheritDoc} */
 218  
     public ByteArrayEncoder appendTextProperty(String name, String value,
 219  
             int index) throws JUploadIOException {
 220  1
         String propertySuffix = "";
 221  
         // if an index is given, we may have to suffix the name of the upload
 222  
         // parameter, depending on the value of httpUploadParameterType
 223  1
         if (index >= 0) {
 224  0
             if (this.uploadPolicy.getHttpUploadParameterType().equals(
 225  
                     UploadPolicy.HTTPUPLOADPARAMETERTYPE_ARRAY)) {
 226  0
                 propertySuffix = "[]";
 227  0
             } else if (this.uploadPolicy.getHttpUploadParameterType().equals(
 228  
                     UploadPolicy.HTTPUPLOADPARAMETERTYPE_ITERATION)) {
 229  0
                 propertySuffix = Integer.toString(index);
 230  
             }
 231  
         }
 232  1
         this.append(this.bound).append("\r\n");
 233  1
         this.append("Content-Disposition: form-data; name=\"").append(name)
 234  1
                 .append(propertySuffix).append("\"\r\n");
 235  1
         this.append("Content-Transfer-Encoding: 8bit\r\n");
 236  1
         this.append("Content-Type: text/plain; charset=").append(this.getEncoding())
 237  1
                 .append("\r\n");
 238  
         // An empty line before the actual value.
 239  1
         this.append("\r\n");
 240  
         // And then, the value!
 241  1
         this.append(value).append("\r\n");
 242  
 
 243  1
         return this;
 244  
     }
 245  
 
 246  
     /** {@inheritDoc} */
 247  
     public ByteArrayEncoder appendEndPropertyList() throws JUploadIOException {
 248  1
         this.append(this.bound).append("--\r\n");
 249  1
         return this;
 250  
     }
 251  
 
 252  
     /** {@inheritDoc} */
 253  
     @SuppressWarnings("restriction")
 254  
     public ByteArrayEncoder appendFormVariables(String formname)
 255  
             throws JUploadIOException {
 256  0
         String action = "Entering ByteArrayEncoderHTTP.appendFormVariables() [html form: "
 257  
                 + formname + "]";
 258  
         try {
 259  0
             this.uploadPolicy.displayDebug(action, 80);
 260  
             // TODO Do not use getApplet() anymore, here.
 261  0
             action = "win = netscape.javascript.JSObject.getWindow";
 262  0
             this.uploadPolicy.displayDebug("Before " + action + 
 263  
                     " (this.uploadPolicy.getContext().getApplet(): "
 264  0
                     + this.uploadPolicy.getContext().getApplet() + ")"
 265  
                     , 80);
 266  0
             netscape.javascript.JSObject win = netscape.javascript.JSObject
 267  0
                     .getWindow(this.uploadPolicy.getContext().getApplet());
 268  0
             this.uploadPolicy.displayDebug("After " + action + 
 269  
                     " (win: " + win + ")", 80);
 270  
 
 271  0
             action = "o = win.eval";
 272  0
             Object o = win.eval("document.forms[\"" + formname
 273  
                     + "\"].elements.length");
 274  0
             if (o instanceof Number) {
 275  0
                 int len = ((Number) o).intValue();
 276  0
                 if (len <= 0) {
 277  0
                     this.uploadPolicy.displayWarn("The specified form \""
 278  
                             + formname + "\" does not contain any elements.");
 279  
                 }
 280  
                 int i;
 281  0
                 for (i = 0; i < len; i++) {
 282  
                     try {
 283  0
                         action = "name = win.eval";
 284  0
                         Object name = win.eval("document.forms[\"" + formname
 285  
                                 + "\"][" + i + "].name");
 286  0
                         action = "value = win.eval";
 287  0
                         Object value = win.eval("document.forms[\"" + formname
 288  
                                 + "\"][" + i + "].value");
 289  0
                         action = "elementType = win.eval";
 290  0
                         Object elementType = win.eval("document.forms[\""
 291  
                                 + formname + "\"][" + i + "].type");
 292  0
                         action = "elementClass = win.eval";
 293  
                         // elementClass seems to be not supported by IE7
 294  
                         // The next line prevents formData to be sent to the
 295  
                         // server, when formData is used on IE7. Works fine with
 296  
                         // firefox.
 297  
                         // Object elementClass = win.eval("document." + formname
 298  
                         // + "[" + i + "].class");
 299  0
                         action = "etype = win.eval";
 300  0
                         Object etype = win.eval("document.forms[\"" + formname
 301  
                                 + "\"][" + i + "].type");
 302  0
                         if (etype instanceof String) {
 303  0
                             String t = (String) etype;
 304  0
                             if (t.equals("checkbox") || t.equals("radio")) {
 305  0
                                 action = "on = win.eval";
 306  0
                                 Object on = win.eval("document.forms[\""
 307  
                                         + formname + "\"][" + i + "].checked");
 308  0
                                 if (on instanceof Boolean) {
 309  
                                     // Skip unchecked checkboxes and
 310  
                                     // radiobuttons
 311  0
                                     if (!((Boolean) on).booleanValue()) {
 312  0
                                         this.uploadPolicy
 313  0
                                                 .displayDebug(
 314  
                                                         "  [ByteArrayEncoder.appendFormVariables] Skipping unchecked checkboxes and radiobuttons",
 315  
                                                         80);
 316  
                                         // TODO Do not use getApplet() anymore,
 317  
                                         // here.
 318  0
                                         continue;
 319  
                                     }
 320  
                                 }
 321  
                             }
 322  
                         }
 323  0
                         if (name instanceof String
 324  0
                                 && ((String) name).length() > 0) {
 325  0
                             if (value instanceof String) {
 326  0
                                 this.uploadPolicy.displayDebug(
 327  
                                         "  [ByteArrayEncoder.appendFormVariables] Adding formdata element num "
 328  
                                                 + i + " (name: " + name
 329  
                                                 + ", value: " + value
 330  
                                                 + ", type: " + elementType
 331  
                                                 /* + ", class: " + elementClass */
 332  
                                                 + ")", 80);
 333  0
                                 this.appendTextProperty((String) name,
 334  
                                         (String) value, -1);
 335  
                             } else {
 336  0
                                 this.uploadPolicy
 337  0
                                         .displayWarn("  [ByteArrayEncoder.appendFormVariables] Value must be an instance of String (name: "
 338  
                                                 + name
 339  
                                                 + ", value: )"
 340  
                                                 + value
 341  
                                                 + ")");
 342  
                             }
 343  
                         } else {
 344  0
                             this.uploadPolicy
 345  0
                                     .displayWarn("  [ByteArrayEncoder.appendFormVariables] Name must be an instance of String (name: "
 346  
                                             + name + ", value: )" + value + ")");
 347  
                         }
 348  0
                     } catch (netscape.javascript.JSException e1) {
 349  0
                         this.uploadPolicy
 350  0
                                 .displayWarn(e1.getStackTrace()[1]
 351  
                                         + ": got JSException in ByteArrayEncoderHTTP.appendFormVariables() [html form: "
 352  
                                         + formname
 353  
                                         + "] - bailing out (action: " + action
 354  
                                         + ")");
 355  0
                         i = len;
 356  0
                     }
 357  
                 }
 358  0
             } else {
 359  0
                 this.uploadPolicy
 360  0
                         .displayWarn("  [ByteArrayEncoder.appendFormVariables] The specified form \""
 361  
                                 + formname
 362  
                                 + "\" could not be found (or has no element).");
 363  
             }
 364  0
         } catch (netscape.javascript.JSException e) {
 365  0
             this.uploadPolicy.displayDebug(e.getStackTrace()[1]
 366  
                     + ": No JavaScript available (action: " + action + ")", 10);
 367  0
         }
 368  
 
 369  0
         this.uploadPolicy
 370  0
                 .displayDebug(
 371  
                         "  [ByteArrayEncoder.appendFormVariables] End of ByteArrayEncoderHTTP.appendFormVariables()",
 372  
                         80);
 373  0
         return this;
 374  
     }
 375  
 
 376  
     /** {@inheritDoc} */
 377  
     public String getBoundary() {
 378  3
         return this.bound;
 379  
     }
 380  
 
 381  
     /**
 382  
      * @return value of the DEFAULT_ENCODING constant.
 383  
      */
 384  
     public static String getDefaultEncoding() {
 385  1
         return DEFAULT_ENCODING;
 386  
     }
 387  
 
 388  
     /** {@inheritDoc} */
 389  
     public boolean isClosed() {
 390  23
         return this.closed;
 391  
     }
 392  
 
 393  
     /** {@inheritDoc} */
 394  
     public String getEncoding() {
 395  17
         return this.encoding;
 396  
     }
 397  
 
 398  
     /** {@inheritDoc} */
 399  
     public int getEncodedLength() throws JUploadIOException {
 400  1
         if (!isClosed()) {
 401  0
             throw new JUploadIOException(
 402  
                     "Trying to get length of a on non-closed ByteArrayEncoded");
 403  
         }
 404  1
         return this.encodedLength;
 405  
     }
 406  
 
 407  
     /** {@inheritDoc} */
 408  
     public byte[] getEncodedByteArray() throws JUploadIOException {
 409  9
         if (!isClosed()) {
 410  1
             throw new JUploadIOException(
 411  
                     "Trying to get the byte array of a on non-closed ByteArrayEncoded");
 412  
         }
 413  8
         return this.encodedByteArray;
 414  
     }
 415  
 
 416  
     /** {@inheritDoc} */
 417  
     public String getString() throws JUploadIOException {
 418  0
         if (!isClosed()) {
 419  0
             throw new JUploadIOException(
 420  
                     "Trying to get the byte array of a on non-closed ByteArrayEncoded");
 421  
         }
 422  
         try {
 423  0
             return new String(this.encodedByteArray, getEncoding());
 424  0
         } catch (UnsupportedEncodingException e) {
 425  0
             throw new JUploadIOException(e);
 426  
         }
 427  
     }
 428  
 
 429  
     // ///////////////////////////////////////////////////////////////////////
 430  
     // //////////////// Private methods //////////////////////////////////////
 431  
     // ///////////////////////////////////////////////////////////////////////
 432  
 
 433  
     /**
 434  
      * Initialization: called by the constructors.
 435  
      * 
 436  
      * @throws JUploadIOException
 437  
      */
 438  
     private void init(UploadPolicy uploadPolicy, String bound, String encoding)
 439  
             throws JUploadIOException {
 440  22
         this.uploadPolicy = uploadPolicy;
 441  22
         this.encoding = encoding;
 442  22
         this.bound = bound;
 443  
 
 444  
         try {
 445  22
             this.writer = new OutputStreamWriter(this.baos, encoding);
 446  0
         } catch (UnsupportedEncodingException e) {
 447  0
             throw new JUploadIOException(e);
 448  22
         }
 449  22
     }
 450  
 }