@Override
  public void decode(FacesContext facesContext, UIComponent uiComponent) {

    InputFile inputFile = (InputFile) uiComponent;

    Map<String, List<UploadedFile>> uploadedFileMap =
        getUploadedFileMap(facesContext, inputFile.getLocation());

    if (uploadedFileMap != null) {

      String clientId = uiComponent.getClientId(facesContext);
      List<UploadedFile> uploadedFiles = uploadedFileMap.get(clientId);

      if ((uploadedFiles != null) && (uploadedFiles.size() > 0)) {

        inputFile.setSubmittedValue(uploadedFiles);

        // Queue the FileUploadEvent so that each uploaded file can be handled individually with an
        // ActionListener.
        for (UploadedFile uploadedFile : uploadedFiles) {

          FileUploadEvent fileUploadEvent = new FileUploadEvent(uiComponent, uploadedFile);
          uiComponent.queueEvent(fileUploadEvent);
        }
      }
    }
  }
  @Override
  protected void encodeHiddenAttributes(
      FacesContext facesContext, ResponseWriter responseWriter, InputFile inputFile, boolean first)
      throws IOException {

    // fileFieldName
    encodeString(responseWriter, "fileFieldName", inputFile.getClientId(), first);
    first = false;

    // multipleFiles
    String multiple = inputFile.getMultiple();
    boolean multipleFiles = "multiple".equalsIgnoreCase(multiple);
    encodeBoolean(responseWriter, "multipleFiles", multipleFiles, first);

    // selectFilesButton
    Locale locale = facesContext.getViewRoot().getLocale();
    String chooseFiles = getMessageContext().getMessage(locale, "choose-files");
    StringBuilder selectFilesButtonScript = new StringBuilder();
    selectFilesButtonScript.append(
        "A.Node.create(\"<button type='button' class='alloy-button' role='button' aria-label='");
    selectFilesButtonScript.append(chooseFiles);
    selectFilesButtonScript.append("' tabindex='{tabIndex}'>");
    selectFilesButtonScript.append(chooseFiles);
    selectFilesButtonScript.append("</button>\")");
    encodeNonEscapedObject(responseWriter, "selectFilesButton", selectFilesButtonScript, first);
  }
  @Override
  public void encodeMarkupEnd(FacesContext facesContext, UIComponent uiComponent)
      throws IOException {

    // If the component should show the progress table, then
    InputFile inputFile = (InputFile) uiComponent;
    ResponseWriter responseWriter = facesContext.getResponseWriter();

    if (inputFile.isShowProgress()) {

      // Finish encoding of the outermost <div> element. Since the template contains its own "Select
      // Files"
      // button, delegation must not occur.
      responseWriter.endElement("div");
    }

    // Otherwise, if the component should show the preview table, then
    else if (inputFile.isShowPreview()) {

      encodePreview(facesContext, responseWriter, inputFile);

      // Finish encoding of the outermost <div> element.
      responseWriter.endElement("div");
    }

    // Otherwise, delegate writing of the entire <input type="file"...> ... </input> element to the
    // delegate
    // renderer.
    else {
      DelegationResponseWriter delegationResponseWriter =
          new InputFileDelegationResponseWriter(responseWriter, inputFile.isAuto());
      super.encodeMarkupEnd(facesContext, uiComponent, delegationResponseWriter);
    }
  }
  protected void encodePreview(
      FacesContext facesContext, ResponseWriter responseWriter, InputFile inputFile)
      throws IOException {

    // Delegate writing of the entire <input type="file"...> ... </input> element to the delegate
    // renderer.
    DelegationResponseWriter delegationResponseWriter =
        new InputFileDelegationResponseWriter(responseWriter, inputFile.isAuto());
    super.encodeMarkupEnd(facesContext, inputFile, delegationResponseWriter);

    // Format the preview-table.html template and write it to the response.
    Locale locale = facesContext.getViewRoot().getLocale();
    String clientId = inputFile.getClientId(facesContext);
    responseWriter.startElement("div", inputFile);
    responseWriter.startElement("table", inputFile);
    responseWriter.writeAttribute("id", clientId + "_table", null);
    responseWriter.writeAttribute("class", "table table-bordered", null);
    responseWriter.startElement("thead", inputFile);
    responseWriter.writeAttribute("class", "table-columns", null);
    responseWriter.startElement("tr", inputFile);
    responseWriter.startElement("th", inputFile);

    MessageContextFactory messageContextFactory =
        (MessageContextFactory) FactoryExtensionFinder.getFactory(MessageContextFactory.class);
    MessageContext messageContext = messageContextFactory.getMessageContext();
    String i18nFileName = messageContext.getMessage(locale, "file-name");
    responseWriter.writeText(i18nFileName, null);
    responseWriter.endElement("th");
    responseWriter.startElement("th", inputFile);

    String i18nFileType = messageContext.getMessage(locale, "file-type");
    responseWriter.writeText(i18nFileType, null);
    responseWriter.endElement("th");
    responseWriter.startElement("th", inputFile);

    String i18nFileSize = messageContext.getMessage(locale, "file-size");
    responseWriter.writeText(i18nFileSize, null);
    responseWriter.endElement("th");
    responseWriter.endElement("tr");
    responseWriter.endElement("thead");
    responseWriter.startElement("tfoot", inputFile);
    responseWriter.startElement("tr", inputFile);
    responseWriter.startElement("td", inputFile);
    responseWriter.writeAttribute("colspan", "3", null);

    String i18nNoFilesSelected = messageContext.getMessage(locale, "no-files-selected");
    responseWriter.writeText(i18nNoFilesSelected, null);
    responseWriter.endElement("td");
    responseWriter.endElement("tr");
    responseWriter.endElement("tfoot");
    responseWriter.startElement("tbody", inputFile);
    responseWriter.startElement("tr", inputFile);
    responseWriter.endElement("tr");
    responseWriter.endElement("tbody");
    responseWriter.endElement("table");
    responseWriter.endElement("div");
  }
  @Override
  public void encodeMarkupBegin(FacesContext facesContext, UIComponent uiComponent)
      throws IOException {
    ResponseWriter responseWriter = facesContext.getResponseWriter();

    InputFile inputFile = (InputFile) uiComponent;

    // If the component should render the preview table or the upload progress table, then
    if (inputFile.isShowPreview() || inputFile.isShowProgress()) {

      // Start encoding the outermost <div> element.
      responseWriter.startElement("div", uiComponent);

      String clientId = uiComponent.getClientId(facesContext);
      responseWriter.writeAttribute("id", clientId, "id");
      RendererUtil.encodeStyleable(responseWriter, (Styleable) uiComponent);

      // If the component should render the upload progress table, then format the
      // progress-table.html template
      // and write it to the response.
      if (inputFile.isShowProgress()) {
        encodeProgress(facesContext, responseWriter, uiComponent, clientId);
      }

      // Otherwise, delegate writing to the delegate renderer. Note that this effectively a no-op
      // with Mojarra and
      // MyFaces, since they both delay writing of the entire <input type="file"...> ... </input>
      // element until
      // encodeEnd.
      else {
        super.encodeMarkupBegin(facesContext, uiComponent);
      }
    }

    // Otherwise, delegate writing to the delegate renderer. Note that this effectively a no-op with
    // Mojarra and
    // MyFaces, since they both delay writing of the entire <input type="file"...> ... </input>
    // element until
    // encodeEnd.
    else {
      super.encodeMarkupBegin(facesContext, uiComponent);
    }
  }
  public AjaxParameters(InputFile inputFile, String clientId, String formClientId) {

    // Default value of execute is "@this" which maps to name and id of the rendered input element.
    this.execute = clientId.concat(StringPool.SPACE).concat(clientId);

    // Default value of render is "@none" which maps to an empty string.
    this.render = StringPool.BLANK;

    // For each Ajax behavior associated with the specified component:
    Map<String, List<ClientBehavior>> clientBehaviorMap = inputFile.getClientBehaviors();
    List<ClientBehavior> clientBehaviors = clientBehaviorMap.get(inputFile.getDefaultEventName());

    for (ClientBehavior clientBehavior : clientBehaviors) {

      if (clientBehavior instanceof AjaxBehavior) {

        // Interpret the value of the f:ajax "execute" attribute.
        AjaxBehavior ajaxBehavior = (AjaxBehavior) clientBehavior;
        Collection<String> executeIds = ajaxBehavior.getExecute();

        if ((executeIds != null) && (executeIds.size() > 0)) {

          boolean foundAllKeyword = false;
          boolean foundNoneKeyword = false;
          StringBuilder buf = new StringBuilder(clientId);

          for (String executeId : executeIds) {

            if ("@all".equals(executeId)) {
              foundAllKeyword = true;

              break;
            } else if ("@none".equals(executeId)) {
              foundNoneKeyword = true;
              this.execute = StringPool.BLANK;

              break;
            } else if (executeId.length() > 0) {

              buf.append(StringPool.SPACE);
              buf.append(executeId);
            }
          }

          if (!foundNoneKeyword) {

            if (foundAllKeyword) {
              this.execute = "@all";
            } else {
              this.execute = buf.toString();
              this.execute = this.execute.replace("@form", formClientId);
              this.execute = this.execute.replace("@this", clientId);

              boolean foundClientId = false;
              String[] executeIdArray = this.execute.split(StringPool.SPACE);

              for (String executeId : executeIdArray) {

                if (clientId.equals(executeId)) {
                  foundClientId = true;

                  break;
                }
              }

              if (!foundClientId) {
                this.execute = clientId.concat(StringPool.SPACE).concat(this.execute);
              }
            }
          }
        }

        // Interpret the value of the f:ajax "render" attribute.
        Collection<String> renderIds = ajaxBehavior.getRender();

        if ((renderIds != null) && (renderIds.size() > 0)) {

          boolean first = true;
          boolean foundAllKeyword = false;
          boolean foundNoneKeyword = false;
          StringBuilder buf = new StringBuilder();

          for (String renderId : renderIds) {

            if ("@all".equals(renderId)) {
              foundAllKeyword = true;

              break;
            } else if ("@none".equals(renderId)) {
              foundNoneKeyword = true;
              this.render = StringPool.BLANK;

              break;
            } else {

              if (renderId.length() > 0) {

                if (first) {
                  first = false;
                } else {
                  buf.append(StringPool.SPACE);
                }

                buf.append(renderId);
              }
            }
          }

          if (!foundNoneKeyword) {

            if (foundAllKeyword) {
              this.render = "@all";
            } else {
              this.render = buf.toString();
              this.render = this.render.replace("@form", formClientId);
              this.render = this.render.replace("@this", clientId);
            }
          }
        }
      }
    }
  }
  @Override
  public void encodeJavaScriptCustom(FacesContext facesContext, UIComponent uiComponent)
      throws IOException {

    ResponseWriter responseWriter = facesContext.getResponseWriter();
    InputFile inputFile = (InputFile) uiComponent;
    JavaScriptFragment alloyNamespace = new JavaScriptFragment("A");

    // Determine the valid content-types and maximum file size from the validator (if specified).
    JavaScriptFragment contentTypes = new JavaScriptFragment("[]");
    String validContentTypes = inputFile.getContentTypes();

    if (validContentTypes != null) {
      contentTypes = toJavaScriptArray(validContentTypes.split(","));
    }

    String clientId = inputFile.getClientId(facesContext);
    Long maxFileSize = inputFile.getMaxFileSize();

    if (maxFileSize == null) {
      maxFileSize = Long.MAX_VALUE;
    }

    // If the component should render the upload progress table, then initialize the YUI progress
    // uploader widget.
    if (inputFile.isShowProgress()) {

      String clientVarName = getClientVarName(facesContext, inputFile);
      String clientKey = inputFile.getClientKey();

      if (clientKey == null) {
        clientKey = clientVarName;
      }

      UIViewRoot viewRoot = facesContext.getViewRoot();
      Locale locale = viewRoot.getLocale();
      String formClientId = getParentFormClientId(inputFile);
      Application application = facesContext.getApplication();
      ViewHandler viewHandler = application.getViewHandler();
      String actionURL = viewHandler.getActionURL(facesContext, viewRoot.getViewId());
      String partialActionURL = facesContext.getExternalContext().encodePartialActionURL(actionURL);
      String namingContainerId = "";

      if (viewRoot instanceof NamingContainer) {
        namingContainerId = viewRoot.getContainerClientId(facesContext);
      }

      AjaxParameters ajaxParameters = new AjaxParameters(inputFile, clientId, formClientId);
      String execute = ajaxParameters.getExecute();
      String render = ajaxParameters.getRender();

      String notStartedMessage = getMessageContext().getMessage(locale, "not-started");
      JavaScriptFragment clientComponent =
          new JavaScriptFragment("Liferay.component('" + clientKey + "')");
      encodeFunctionCall(
          responseWriter,
          "LFAI.initProgressUploader",
          alloyNamespace,
          clientComponent,
          contentTypes,
          clientId,
          formClientId,
          namingContainerId,
          inputFile.isAuto(),
          execute,
          render,
          partialActionURL,
          maxFileSize,
          notStartedMessage);
    }

    // Otherwise, if the component should render the upload preview table, then format the
    // preview-uploader.js
    // template and write it to the response.
    else if (inputFile.isShowPreview()) {

      encodeFunctionCall(
          responseWriter,
          "LFAI.initPreviewUploader",
          alloyNamespace,
          contentTypes,
          clientId,
          maxFileSize);
    }
  }