/**
  * Split the very first line (Content-Type value) in 3 Strings
  *
  * @return the array of 3 Strings
  */
 private static String[] splitHeaderContentType(String sb) {
   int aStart;
   int aEnd;
   int bStart;
   int bEnd;
   int cStart;
   int cEnd;
   aStart = HttpPostBodyUtil.findNonWhitespace(sb, 0);
   aEnd = sb.indexOf(';');
   if (aEnd == -1) {
     return new String[] {sb, "", ""};
   }
   bStart = HttpPostBodyUtil.findNonWhitespace(sb, aEnd + 1);
   if (sb.charAt(aEnd - 1) == ' ') {
     aEnd--;
   }
   bEnd = sb.indexOf(';', bStart);
   if (bEnd == -1) {
     bEnd = HttpPostBodyUtil.findEndOfString(sb);
     return new String[] {sb.substring(aStart, aEnd), sb.substring(bStart, bEnd), ""};
   }
   cStart = HttpPostBodyUtil.findNonWhitespace(sb, bEnd + 1);
   if (sb.charAt(bEnd - 1) == ' ') {
     bEnd--;
   }
   cEnd = HttpPostBodyUtil.findEndOfString(sb);
   return new String[] {
     sb.substring(aStart, aEnd), sb.substring(bStart, bEnd), sb.substring(cStart, cEnd)
   };
 }
 /**
  * Find the next Multipart Delimiter
  *
  * @param delimiter delimiter to find
  * @param dispositionStatus the next status if the delimiter is a start
  * @param closeDelimiterStatus the next status if the delimiter is a close delimiter
  * @return the next InterfaceHttpData if any
  * @throws ErrorDataDecoderException
  */
 private InterfaceHttpData findMultipartDelimiter(
     String delimiter, MultiPartStatus dispositionStatus, MultiPartStatus closeDelimiterStatus)
     throws ErrorDataDecoderException {
   // --AaB03x or --AaB03x--
   int readerIndex = undecodedChunk.readerIndex();
   HttpPostBodyUtil.skipControlCharacters(undecodedChunk);
   skipOneLine();
   String newline;
   try {
     newline = readLine();
   } catch (NotEnoughDataDecoderException e) {
     undecodedChunk.readerIndex(readerIndex);
     return null;
   }
   if (newline.equals(delimiter)) {
     currentStatus = dispositionStatus;
     return decodeMultipart(dispositionStatus);
   } else if (newline.equals(delimiter + "--")) {
     // CLOSEDELIMITER or MIXED CLOSEDELIMITER found
     currentStatus = closeDelimiterStatus;
     if (currentStatus == MultiPartStatus.HEADERDELIMITER) {
       // MIXEDCLOSEDELIMITER
       // end of the Mixed part
       currentFieldAttributes = null;
       return decodeMultipart(MultiPartStatus.HEADERDELIMITER);
     }
     return null;
   }
   undecodedChunk.readerIndex(readerIndex);
   throw new ErrorDataDecoderException("No Multipart delimiter found");
 }
 /**
  * Split the very first line (Content-Type value) in 2 Strings
  *
  * @param sb
  * @return the array of 2 Strings
  */
 private String[] splitHeaderContentType(String sb) {
   int size = sb.length();
   int aStart;
   int aEnd;
   int bStart;
   int bEnd;
   aStart = HttpPostBodyUtil.findNonWhitespace(sb, 0);
   aEnd = HttpPostBodyUtil.findWhitespace(sb, aStart);
   if (aEnd >= size) {
     return new String[] {sb, ""};
   }
   if (sb.charAt(aEnd) == ';') {
     aEnd--;
   }
   bStart = HttpPostBodyUtil.findNonWhitespace(sb, aEnd);
   bEnd = HttpPostBodyUtil.findEndOfString(sb);
   return new String[] {sb.substring(aStart, aEnd), sb.substring(bStart, bEnd)};
 }
 /**
  * Split one header in Multipart
  *
  * @param sb
  * @return an array of String where rank 0 is the name of the header, follows by several values
  *     that were separated by ';' or ','
  */
 private String[] splitMultipartHeader(String sb) {
   ArrayList<String> headers = new ArrayList<String>(1);
   int nameStart;
   int nameEnd;
   int colonEnd;
   int valueStart;
   int valueEnd;
   nameStart = HttpPostBodyUtil.findNonWhitespace(sb, 0);
   for (nameEnd = nameStart; nameEnd < sb.length(); nameEnd++) {
     char ch = sb.charAt(nameEnd);
     if (ch == ':' || Character.isWhitespace(ch)) {
       break;
     }
   }
   for (colonEnd = nameEnd; colonEnd < sb.length(); colonEnd++) {
     if (sb.charAt(colonEnd) == ':') {
       colonEnd++;
       break;
     }
   }
   valueStart = HttpPostBodyUtil.findNonWhitespace(sb, colonEnd);
   valueEnd = HttpPostBodyUtil.findEndOfString(sb);
   headers.add(sb.substring(nameStart, nameEnd));
   String svalue = sb.substring(valueStart, valueEnd);
   String[] values = null;
   if (svalue.indexOf(";") >= 0) {
     values = svalue.split(";");
   } else {
     values = svalue.split(",");
   }
   for (String value : values) {
     headers.add(value.trim());
   }
   String[] array = new String[headers.size()];
   for (int i = 0; i < headers.size(); i++) {
     array[i] = headers.get(i);
   }
   return array;
 }
 /**
  * Find the next Disposition
  *
  * @return the next InterfaceHttpData if any
  * @throws ErrorDataDecoderException
  */
 private InterfaceHttpData findMultipartDisposition() throws ErrorDataDecoderException {
   int readerIndex = undecodedChunk.readerIndex();
   if (currentStatus == MultiPartStatus.DISPOSITION) {
     currentFieldAttributes = new TreeMap<String, Attribute>(CaseIgnoringComparator.INSTANCE);
   }
   // read many lines until empty line with newline found! Store all data
   while (!skipOneLine()) {
     HttpPostBodyUtil.skipControlCharacters(undecodedChunk);
     String newline;
     try {
       newline = readLine();
     } catch (NotEnoughDataDecoderException e) {
       undecodedChunk.readerIndex(readerIndex);
       return null;
     }
     String[] contents = splitMultipartHeader(newline);
     if (contents[0].equalsIgnoreCase(HttpPostBodyUtil.CONTENT_DISPOSITION)) {
       boolean checkSecondArg = false;
       if (currentStatus == MultiPartStatus.DISPOSITION) {
         checkSecondArg = contents[1].equalsIgnoreCase(HttpPostBodyUtil.FORM_DATA);
       } else {
         checkSecondArg =
             contents[1].equalsIgnoreCase(HttpPostBodyUtil.ATTACHMENT)
                 || contents[1].equalsIgnoreCase(HttpPostBodyUtil.FILE);
       }
       if (checkSecondArg) {
         // read next values and store them in the map as Attribute
         for (int i = 2; i < contents.length; i++) {
           String[] values = contents[i].split("=");
           Attribute attribute;
           try {
             attribute =
                 factory.createAttribute(
                     request, values[0].trim(), decodeAttribute(cleanString(values[1]), charset));
           } catch (NullPointerException e) {
             throw new ErrorDataDecoderException(e);
           } catch (IllegalArgumentException e) {
             throw new ErrorDataDecoderException(e);
           }
           currentFieldAttributes.put(attribute.getName(), attribute);
         }
       }
     } else if (contents[0].equalsIgnoreCase(HttpHeaders.Names.CONTENT_TRANSFER_ENCODING)) {
       Attribute attribute;
       try {
         attribute =
             factory.createAttribute(
                 request, HttpHeaders.Names.CONTENT_TRANSFER_ENCODING, cleanString(contents[1]));
       } catch (NullPointerException e) {
         throw new ErrorDataDecoderException(e);
       } catch (IllegalArgumentException e) {
         throw new ErrorDataDecoderException(e);
       }
       currentFieldAttributes.put(HttpHeaders.Names.CONTENT_TRANSFER_ENCODING, attribute);
     } else if (contents[0].equalsIgnoreCase(HttpHeaders.Names.CONTENT_LENGTH)) {
       Attribute attribute;
       try {
         attribute =
             factory.createAttribute(
                 request, HttpHeaders.Names.CONTENT_LENGTH, cleanString(contents[1]));
       } catch (NullPointerException e) {
         throw new ErrorDataDecoderException(e);
       } catch (IllegalArgumentException e) {
         throw new ErrorDataDecoderException(e);
       }
       currentFieldAttributes.put(HttpHeaders.Names.CONTENT_LENGTH, attribute);
     } else if (contents[0].equalsIgnoreCase(HttpHeaders.Names.CONTENT_TYPE)) {
       // Take care of possible "multipart/mixed"
       if (contents[1].equalsIgnoreCase(HttpPostBodyUtil.MULTIPART_MIXED)) {
         if (currentStatus == MultiPartStatus.DISPOSITION) {
           String[] values = contents[2].split("=");
           multipartMixedBoundary = "--" + values[1];
           currentStatus = MultiPartStatus.MIXEDDELIMITER;
           return decodeMultipart(MultiPartStatus.MIXEDDELIMITER);
         } else {
           throw new ErrorDataDecoderException(
               "Mixed Multipart found in a previous Mixed Multipart");
         }
       } else {
         for (int i = 1; i < contents.length; i++) {
           if (contents[i].toLowerCase().startsWith(HttpHeaders.Values.CHARSET)) {
             String[] values = contents[i].split("=");
             Attribute attribute;
             try {
               attribute =
                   factory.createAttribute(
                       request, HttpHeaders.Values.CHARSET, cleanString(values[1]));
             } catch (NullPointerException e) {
               throw new ErrorDataDecoderException(e);
             } catch (IllegalArgumentException e) {
               throw new ErrorDataDecoderException(e);
             }
             currentFieldAttributes.put(HttpHeaders.Values.CHARSET, attribute);
           } else {
             Attribute attribute;
             try {
               attribute =
                   factory.createAttribute(
                       request,
                       contents[0].trim(),
                       decodeAttribute(cleanString(contents[i]), charset));
             } catch (NullPointerException e) {
               throw new ErrorDataDecoderException(e);
             } catch (IllegalArgumentException e) {
               throw new ErrorDataDecoderException(e);
             }
             currentFieldAttributes.put(attribute.getName(), attribute);
           }
         }
       }
     } else {
       throw new ErrorDataDecoderException("Unknown Params: " + newline);
     }
   }
   // Is it a FileUpload
   Attribute filenameAttribute = currentFieldAttributes.get(HttpPostBodyUtil.FILENAME);
   if (currentStatus == MultiPartStatus.DISPOSITION) {
     if (filenameAttribute != null) {
       // FileUpload
       currentStatus = MultiPartStatus.FILEUPLOAD;
       // do not change the buffer position
       return decodeMultipart(MultiPartStatus.FILEUPLOAD);
     } else {
       // Field
       currentStatus = MultiPartStatus.FIELD;
       // do not change the buffer position
       return decodeMultipart(MultiPartStatus.FIELD);
     }
   } else {
     if (filenameAttribute != null) {
       // FileUpload
       currentStatus = MultiPartStatus.MIXEDFILEUPLOAD;
       // do not change the buffer position
       return decodeMultipart(MultiPartStatus.MIXEDFILEUPLOAD);
     } else {
       // Field is not supported in MIXED mode
       throw new ErrorDataDecoderException("Filename not found");
     }
   }
 }