/**
   * Private helper method to get the name of the output message class for a FOperation object. The
   * method will return null, if the operation has no output message at all.
   *
   * @param operation FOperation object from WSDL parser
   * @return Output message class name or null
   */
  private String getOutputMessageName(final FOperation operation) {
    String result = null;

    // Operation has output message?
    if (null != operation.getOutputMessage()) {
      result =
          WorkerThreadGenerator.firstLetterCapital(
                  operation.getOutputMessage().getMessageAttribute().getLocalPart())
              + "Message";
    }

    return result;
  }
  /**
   * Create a file that contains a single worker thread class. All child worker classes are
   * generated on a per-operation basis for each and every RPC method defined in the WSDL document.
   *
   * <p>This method will trigger the class generation and add all required Java imports to the
   * source file.
   *
   * @param operation FOperation object from WSDL parser
   * @throws Exception Error during code generation
   */
  private void createWorkerThreadFile(final FOperation operation) throws Exception {
    String rpcMethodName = WorkerThreadGenerator.firstLetterCapital(operation.getOperationName());
    JSourceFile jsf =
        this.workspace
            .getJava()
            .getJSourceFile(this.threadWorkerPackageName, rpcMethodName + "Worker");

    // Add child worker thread class
    jsf.add(this.createWorkerThreadClass(operation));

    // Add required imports
    this.addRequiredImport(jsf, "org.slf4j.Logger");
    this.addRequiredImport(jsf, "org.slf4j.LoggerFactory");
    this.addRequiredImport(jsf, "org.atmosphere.websocket.WebSocket");

    this.addRequiredImport(
        jsf, this.serviceProviderPackageName + "." + this.serviceProviderClassName);
    this.addRequiredImport(jsf, this.packageName + "." + this.interfaceName);
    this.addRequiredImport(
        jsf, this.packageName + "." + JSONMarshallerGenerator.MARSHALLER_CLASS_NAME);

    // Import server only if required
    if (null != operation.getOutputMessage()) {
      this.addRequiredImport(jsf, this.packageName + "." + this.interfaceName);
    }

    // Operation has input message
    if (null != operation.getInputMessage()) {
      this.addRequiredImport(
          jsf, this.serviceProviderPackageName + "." + this.getInputMessageName(operation));
    }

    // Operation has output message
    if (null != operation.getOutputMessage()) {
      this.addRequiredImport(
          jsf, this.serviceProviderPackageName + "." + this.getOutputMessageName(operation));
    }
  }
  /**
   * Create code for method body of the run() method that is generated in each of the child thread
   * worker classes. This function will generate code to unmarshal a request message, call the RPC
   * method and finally marshal the response message (if RPC method has any output).
   *
   * <p>The code generation was moved to this method for better readability.
   *
   * @param operation FOperation object from WSDL parser
   * @param rpcMethodName Name of RPC method
   * @return Code for method body of run() method
   */
  private String createRunMethodBody(final FOperation operation, final String rpcMethodName) {
    String methodBody = "";

    // Create JSON marshaller object
    if (null != operation.getInputMessage() || null != operation.getOutputMessage()) {
      methodBody +=
          String.format(
              "// Create JSON marshaller\n" + "%s marshaller = new %s();\n\n",
              JSONMarshallerGenerator.MARSHALLER_CLASS_NAME,
              JSONMarshallerGenerator.MARSHALLER_CLASS_NAME);
    }

    // Operation has input message?
    if (null != operation.getInputMessage()) {
      String inputMessageClassName = this.getInputMessageName(operation);

      // Create code to convert JSON code to a bean object
      methodBody +=
          String.format(
              "// Unmarshal JSON code from request\n"
                  + "%s requestBeanObject = (%s)marshaller.jsonToInstance(%s.class, this.requestMessage.payload());\n\n",
              inputMessageClassName, inputMessageClassName, inputMessageClassName);
    }

    // Get name of output message (if any)
    String outputMessageClassName = "";
    if (null != operation.getOutputMessage()) {
      outputMessageClassName = this.getOutputMessageName(operation);
    }

    // Create code to call RPC method
    methodBody +=
        String.format(
            "// Call service operation\n"
                + "LOGGER.info(\"Processing '%s()' request...\");\n"
                + "%sthis.serviceProvider.%s(%s);",
            WorkerThreadGenerator.firstLetterLowercase(rpcMethodName),
            (null != operation.getOutputMessage()
                ? String.format("%s responseBeanObject = ", outputMessageClassName)
                : ""), // Method has return value?
            operation.getOperationName(),
            (null != operation.getInputMessage() ? "requestBeanObject" : "")); // Method has input?

    // Operation has output message?
    if (null != operation.getOutputMessage()) {
      // Create code to convert bean object to JSON code
      methodBody +=
          String.format(
              "\n\n"
                  + "// Marshal bean and create response message\n"
                  + "String jsonResponse = marshaller.instanceToJSON(responseBeanObject);\n"
                  + "%s responseMessage = new %s(this.requestMessage.uuid(), this.requestMessage.method(), jsonResponse);\n\n",
              this.messageClassFullName, this.messageClassFullName);

      // Create code to send response
      methodBody +=
          String.format(
              "// Send response to client\n"
                  + "LOGGER.info(\"Responding to '%s()' request...\");\n"
                  + "%s.sendMessage(this.webSocket, responseMessage.asString());",
              WorkerThreadGenerator.firstLetterLowercase(rpcMethodName), this.interfaceName);
    }

    // Surround code with try..catch-block
    methodBody =
        String.format(
            "try {\n"
                + WorkerThreadGenerator.indentCode(methodBody)
                + "}\n"
                + "catch (Exception e) {\n"
                + "\tString jsonResponse = String.format(\"{ \\\"Error\\\": \\\"%%s\\\" }\", e.getMessage());\n"
                + "\t%s responseMessage = new %s(this.requestMessage.uuid(), this.requestMessage.method(), jsonResponse);\n\n"
                + "\tLOGGER.error(\"Error: \" + e.getMessage());\n"
                + "\t%s.sendMessage(this.webSocket, responseMessage.asString());\n"
                + "}",
            this.messageClassFullName,
            this.messageClassFullName,
            this.interfaceName);

    return methodBody;
  }