/*
 * Koekiebox CONFIDENTIAL
 *
 * [2012] - [2017] Koekiebox (Pty) Ltd
 * All Rights Reserved.
 *
 * NOTICE: All information contained herein is, and remains the property
 * of Koekiebox and its suppliers, if any. The intellectual and
 * technical concepts contained herein are proprietary to Koekiebox
 * and its suppliers and may be covered by South African and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material is strictly
 * forbidden unless prior written permission is obtained from Koekiebox.
 */

package com.fluidbpm.ws.client.v1.flow;

import java.util.List;

import org.json.JSONObject;

import com.fluidbpm.program.api.util.UtilGlobal;
import com.fluidbpm.program.api.vo.flow.*;
import com.fluidbpm.program.api.vo.item.FluidItem;
import com.fluidbpm.program.api.vo.ws.WS;
import com.fluidbpm.ws.client.v1.ABaseClientWS;

/**
 * Used to change any of the Flow rules and
 * underlying steps and rules.
 *
 * This is ideal for doing automated tests against
 * the Fluid platform.
 *
 * @author jasonbruwer
 * @since v1.0
 *
 * @see JSONObject
 * @see com.fluidbpm.program.api.vo.ws.WS.Path.FlowStepRule
 * @see FlowStepRule
 * @see ABaseClientWS
 */
public class FlowStepRuleClient extends ABaseClientWS {

    /**
     * Constructor that sets the Service Ticket from authentication.
     *
     * @param endpointBaseUrlParam URL to base endpoint.
     * @param serviceTicketParam The Server issued Service Ticket.
     */
    public FlowStepRuleClient(
            String endpointBaseUrlParam,
            String serviceTicketParam) {
        super(endpointBaseUrlParam);

        this.setServiceTicket(serviceTicketParam);
    }

    /**
     * Create a new Flow Step Entry rule.
     *
     * @param flowStepRuleParam Rule to create.
     * @return Created rule.
     */
    public FlowStepRule createFlowStepEntryRule(FlowStepRule flowStepRuleParam)
    {
        if(flowStepRuleParam != null && this.serviceTicket != null)
        {
            flowStepRuleParam.setServiceTicket(this.serviceTicket);
        }

        return new FlowStepRule(this.putJson(
                flowStepRuleParam, WS.Path.FlowStepRule.Version1.flowStepRuleEntryCreate()));
    }

    /**
     * Create a new Flow Step Exit rule.
     *
     * @param flowStepRuleParam Rule to create.
     * @return Created rule.
     */
    public FlowStepRule createFlowStepExitRule(FlowStepRule flowStepRuleParam)
    {
        if(flowStepRuleParam != null && this.serviceTicket != null)
        {
            flowStepRuleParam.setServiceTicket(this.serviceTicket);
        }

        return new FlowStepRule(this.putJson(
                flowStepRuleParam, WS.Path.FlowStepRule.Version1.flowStepRuleExitCreate()));
    }

    /**
     * Create a new Flow Step View rule.
     *
     * @param flowStepRuleParam Rule to create.
     * @return Created rule.
     */
    public FlowStepRule createFlowStepViewRule(FlowStepRule flowStepRuleParam)
    {
        if(flowStepRuleParam != null && this.serviceTicket != null)
        {
            flowStepRuleParam.setServiceTicket(this.serviceTicket);
        }

        return new FlowStepRule(this.putJson(
                flowStepRuleParam, WS.Path.FlowStepRule.Version1.flowStepRuleViewCreate()));
    }

    /**
     * Update an existing Flow Step Entry rule.
     *
     * @param flowStepRuleParam Rule to update.
     * @return Updated rule.
     */
    public FlowStepRule updateFlowStepEntryRule(FlowStepRule flowStepRuleParam)
    {
        if(flowStepRuleParam != null && this.serviceTicket != null)
        {
            flowStepRuleParam.setServiceTicket(this.serviceTicket);
        }

        return new FlowStepRule(this.postJson(
                flowStepRuleParam, WS.Path.FlowStepRule.Version1.flowStepRuleUpdateEntry()));
    }

    /**
     * Update an existing Flow Step Exit rule.
     *
     * @param flowStepRuleParam Rule to update.
     * @return Updated rule.
     */
    public FlowStepRule updateFlowStepExitRule(FlowStepRule flowStepRuleParam)
    {
        if(flowStepRuleParam != null && this.serviceTicket != null)
        {
            flowStepRuleParam.setServiceTicket(this.serviceTicket);
        }

        return new FlowStepRule(this.postJson(
                flowStepRuleParam, WS.Path.FlowStepRule.Version1.flowStepRuleUpdateExit()));
    }

    /**
     * Update an existing Flow Step View rule.
     *
     * @param flowStepRuleParam Rule to update.
     * @return Updated rule.
     */
    public FlowStepRule updateFlowStepViewRule(FlowStepRule flowStepRuleParam)
    {
        if(flowStepRuleParam != null && this.serviceTicket != null)
        {
            flowStepRuleParam.setServiceTicket(this.serviceTicket);
        }

        return new FlowStepRule(this.postJson(
                flowStepRuleParam, WS.Path.FlowStepRule.Version1.flowStepRuleUpdateView()));
    }

    /**
     * Compiles the {@code viewRuleSyntaxParam} text within the Fluid workflow engine.
     *
     * @param viewRuleSyntaxParam The syntax to compile.
     * @return Compiled rule.
     */
    public FlowStepRule compileFlowStepViewRule(String viewRuleSyntaxParam)
    {
        FlowStepRule flowStepRule = new FlowStepRule();
        flowStepRule.setRule(viewRuleSyntaxParam);

        if(this.serviceTicket != null)
        {
            flowStepRule.setServiceTicket(this.serviceTicket);
        }

        return new FlowStepRule(this.postJson(
                flowStepRule, WS.Path.FlowStepRule.Version1.compileViewSyntax()));
    }

    /**
     * Retrieves the exit rules by step {@code flowStepParam}.
     * Name or id can be provided.
     *
     * @param flowStepParam The flow step to get the exit rules for.
     * @return All the exit rules for the step {@code flowStepParam}.
     */
    public FlowStepRuleListing getExitRulesByStep(FlowStep flowStepParam)
    {
        if(flowStepParam == null)
        {
            return null;
        }

        if(this.serviceTicket != null)
        {
            flowStepParam.setServiceTicket(this.serviceTicket);
        }

        return new FlowStepRuleListing(this.postJson(
                flowStepParam, WS.Path.FlowStepRule.Version1.getExitRulesByStep()));
    }

    /**
     * Retrieves the entry rules by step {@code flowStepParam}.
     * Name or id can be provided.
     *
     * @param flowStepParam The flow step to get the exit rules for.
     * @return All the entry rules for the step {@code flowStepParam}.
     */
    public FlowStepRuleListing getEntryRulesByStep(FlowStep flowStepParam)
    {
        if(flowStepParam == null)
        {
            return null;
        }

        if(this.serviceTicket != null)
        {
            flowStepParam.setServiceTicket(this.serviceTicket);
        }

        return new FlowStepRuleListing(this.postJson(
                flowStepParam, WS.Path.FlowStepRule.Version1.getEntryRulesByStep()));
    }

    /**
     * Compiles and Executes the {@code viewRuleSyntaxParam}
     * text within the Fluid workflow engine.
     *
     * @param viewRuleSyntaxParam The syntax to compile.
     * @param fluidItemToExecuteOnParam The item to execute the rules on.
     *
     * @return Execution result.
     */
    public FlowItemExecuteResult compileFlowStepViewRuleAndExecute(
            String viewRuleSyntaxParam, FluidItem fluidItemToExecuteOnParam)
    {
        FlowStepRule flowStepRule = new FlowStepRule();
        flowStepRule.setRule(viewRuleSyntaxParam);

        FlowItemExecutePacket toPost = new FlowItemExecutePacket();

        if(this.serviceTicket != null)
        {
            toPost.setServiceTicket(this.serviceTicket);
        }

        toPost.setFlowStepRule(flowStepRule);
        toPost.setFluidItem(fluidItemToExecuteOnParam);

        return new FlowItemExecuteResult(this.postJson(
                toPost, WS.Path.FlowStepRule.Version1.compileViewSyntaxAndExecute()));
    }

    /**
     * Compiles the {@code entryRuleSyntaxParam} text within the Fluid workflow engine.
     *
     * @param entryRuleSyntaxParam The syntax to compile.
     * @return Compiled rule.
     */
    public FlowStepRule compileFlowStepEntryRule(String entryRuleSyntaxParam)
    {
        FlowStepRule flowStepRule = new FlowStepRule();
        flowStepRule.setRule(entryRuleSyntaxParam);

        if(this.serviceTicket != null)
        {
            flowStepRule.setServiceTicket(this.serviceTicket);
        }

        return new FlowStepRule(this.postJson(
                flowStepRule, WS.Path.FlowStepRule.Version1.compileEntrySyntax()));
    }

    /**
     * Compiles and Executes the {@code entryRuleSyntaxParam}
     * text within the Fluid workflow engine.
     *
     * @param entryRuleSyntaxParam The syntax to compile.
     * @param fluidItemToExecuteOnParam The item to execute the rules on.
     *
     * @return Execution result.
     */
    public FlowItemExecuteResult compileFlowStepEntryRuleAndExecute(
            String entryRuleSyntaxParam, FluidItem fluidItemToExecuteOnParam)
    {
        FlowStepRule flowStepRule = new FlowStepRule();
        flowStepRule.setRule(entryRuleSyntaxParam);

        FlowItemExecutePacket toPost = new FlowItemExecutePacket();

        if(this.serviceTicket != null)
        {
            toPost.setServiceTicket(this.serviceTicket);
        }

        toPost.setFlowStepRule(flowStepRule);
        toPost.setFluidItem(fluidItemToExecuteOnParam);

        return new FlowItemExecuteResult(this.postJson(
                toPost, WS.Path.FlowStepRule.Version1.compileEntrySyntaxAndExecute()));
    }

    /**
     * Moves an entry rule order one up from the current location.
     *
     * @param flowStepRuleParam The Rule to move up.
     * @return The result after the move.
     */
    public FlowStepRule moveFlowStepEntryRuleUp(FlowStepRule flowStepRuleParam)
    {
        if(flowStepRuleParam != null && this.serviceTicket != null)
        {
            flowStepRuleParam.setServiceTicket(this.serviceTicket);
        }

        return new FlowStepRule(this.postJson(
                flowStepRuleParam, WS.Path.FlowStepRule.Version1.flowStepRuleMoveEntryUp()));
    }

    /**
     * Moves an entry rule order one down from the current location.
     *
     * @param flowStepRuleParam The Rule to move down.
     * @return The result after the move.
     */
    public FlowStepRule moveFlowStepEntryRuleDown(FlowStepRule flowStepRuleParam)
    {
        if(flowStepRuleParam != null && this.serviceTicket != null)
        {
            flowStepRuleParam.setServiceTicket(this.serviceTicket);
        }

        return new FlowStepRule(this.postJson(
                flowStepRuleParam, WS.Path.FlowStepRule.Version1.flowStepRuleMoveEntryDown()));
    }

    /**
     * Deletes an Step Entry rule.
     *
     * @param flowStepRuleParam The rule to delete.
     * @return The deleted rule.
     */
    public FlowStepRule deleteFlowStepEntryRule(FlowStepRule flowStepRuleParam)
    {
        if(flowStepRuleParam != null && this.serviceTicket != null)
        {
            flowStepRuleParam.setServiceTicket(this.serviceTicket);
        }

        return new FlowStepRule(this.postJson(
                flowStepRuleParam, WS.Path.FlowStepRule.Version1.flowStepRuleDeleteEntry()));
    }

    /**
     * Deletes an Step Exit rule.
     *
     * @param flowStepRuleParam The rule to delete.
     * @return The deleted rule.
     */
    public FlowStep deleteFlowStepExitRule(FlowStepRule flowStepRuleParam)
    {
        if(flowStepRuleParam != null && this.serviceTicket != null)
        {
            flowStepRuleParam.setServiceTicket(this.serviceTicket);
        }

        return new FlowStep(this.postJson(
                flowStepRuleParam, WS.Path.FlowStepRule.Version1.flowStepRuleDeleteExit()));
    }

    /**
     * Deletes an Step View rule.
     *
     * @param flowStepRuleParam The rule to delete.
     * @return The deleted rule.
     */
    public FlowStep deleteFlowStepViewRule(FlowStepRule flowStepRuleParam)
    {
        if(flowStepRuleParam != null && this.serviceTicket != null)
        {
            flowStepRuleParam.setServiceTicket(this.serviceTicket);
        }

        return new FlowStep(this.postJson(
                flowStepRuleParam, WS.Path.FlowStepRule.Version1.flowStepRuleDeleteView()));
    }

    /**
     * Retrieves the next valid syntax rules for {@code inputRuleParam}.
     *
     * @param inputRuleParam The text to use as input.
     * @return Listing of valid syntax words to use.
     */
    public List<String> getNextValidSyntaxWordsEntryRule(String inputRuleParam)
    {
        if(inputRuleParam == null)
        {
            inputRuleParam = UtilGlobal.EMPTY;
        }

        FlowStepRule flowStepRule = new FlowStepRule();
        flowStepRule.setRule(inputRuleParam);

        if(this.serviceTicket != null)
        {
            flowStepRule.setServiceTicket(this.serviceTicket);
        }

        FlowStepRule returnedObj = new FlowStepRule(this.postJson(
                        flowStepRule, WS.Path.FlowStepRule.Version1.getNextValidEntrySyntax()));

        return returnedObj.getNextValidSyntaxWords();
    }

    /**
     * Retrieves the next valid syntax rules for {@code inputRuleParam}.
     *
     * @param inputRuleParam The text to use as input.
     * @return Listing of valid syntax words to use.
     */
    public List<String> getNextValidSyntaxWordsExitRule(String inputRuleParam)
    {
        if(inputRuleParam == null)
        {
            inputRuleParam = UtilGlobal.EMPTY;
        }

        FlowStepRule flowStepRule = new FlowStepRule();
        flowStepRule.setRule(inputRuleParam);

        if(this.serviceTicket != null)
        {
            flowStepRule.setServiceTicket(this.serviceTicket);
        }

        FlowStepRule returnedObj = new FlowStepRule(this.postJson(
                flowStepRule, WS.Path.FlowStepRule.Version1.getNextValidExitSyntax()));

        return returnedObj.getNextValidSyntaxWords();
    }
}


/*
 * Koekiebox CONFIDENTIAL
 *
 * [2012] - [2017] Koekiebox (Pty) Ltd
 * All Rights Reserved.
 *
 * NOTICE: All information contained herein is, and remains the property
 * of Koekiebox and its suppliers, if any. The intellectual and
 * technical concepts contained herein are proprietary to Koekiebox
 * and its suppliers and may be covered by South African and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material is strictly
 * forbidden unless prior written permission is obtained from Koekiebox.
 */
package com.fluidbpm.ws.client.v1.user;

import java.util.Arrays;
import java.util.concurrent.TimeUnit;

import org.json.JSONException;
import org.json.JSONObject;

import com.fluidbpm.program.api.util.UtilGlobal;
import com.fluidbpm.program.api.vo.ws.WS;
import com.fluidbpm.program.api.vo.ws.auth.AppRequestToken;
import com.fluidbpm.program.api.vo.ws.auth.AuthEncryptedData;
import com.fluidbpm.program.api.vo.ws.auth.AuthRequest;
import com.fluidbpm.program.api.vo.ws.auth.AuthResponse;
import com.fluidbpm.ws.client.FluidClientException;
import com.fluidbpm.ws.client.v1.ABaseClientWS;

/**
 * Java Web Service Client for User Login related actions.
 *
 * The API is based on the Kerberos mutual authentication model.

 * See https://en.wikipedia.org/wiki/Kerberos_(protocol).
 *
 * Sequence for login is as follows;
 *
 *

 *
1. Pre Checks;

 *

 *
 * The API requires the following prerequisites;
 *

 *

 *
 *

 *
Support for TLS 1.0 and above (For public / private RSA connection).

 *
SHA-256 hashing function (For password and HMAC).

 *
AES-256 (Session keys and encrypted data).

 *

 *
 * If Java is being used, the Unlimited Strength Jurisdiction Policy Files needs to be used
 * to enable AES-256. See http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html
 *
 *

 *
2. Authentication Steps

 *
 *     The steps below explains the process of authenticating against the Fluid-API.
 *

 *
 *
2.1 Authentication Request and Response

 *
 * 2.1.1. Send a {@code AuthRequest} JSON request {@code POST} to;

 *     {@code https://<server:port>/fluid-ws/v1/user/init}

 *         to initiate the authentication.

 * * Note: The lifetime is in seconds.

 *
 * Example of {@code AuthRequest} request JSON;

 *
 *  {

 *           "username" : "johnny",

 *           "lifetime", 1000

 *  }

 *
 * Breakdown of {@code AuthRequest};
 *

 *

 *
{@code username} : The Fluid username.

 *
{@code lifetime} : The duration of the Service Ticket in seconds.

 *

 *
 *

 *
 *
 * 2.1.2. If the provided {@code AuthRequest} JSON is correct, the Fluid-API will return a {@code AuthResponse} in JSON format.
 *

 *

 *

 *
 * Example of {@code AuthResponse} response JSON;

 *
 *
 *  {

 *      "salt": "jQJVm'T7zhG1d'z)!o!+",

 *      "encryptedDataBase64": "IeWj7pcXhLXf2lEFtRVU",

 *      "encryptedDataHmacBase64": "SdAU3W/VDk5CNCMlZL0k=",

 *      "ivBase64": "cCWtbV+K2J4peTNx/7EZ5w==",

 *      "seedBase64": "BRJyFMvUIm/FZq6VxaIzYC9nYnZCZmWEuBARuUQ5JTw=",

 *      "serviceTicketBase64": "of6torcGONT6kjyNE3+pVutWZ4cz/eTWGsbP"

 *  }

 *
 *
 *

 *
 * Breakdown of {@code AuthResponse};
 *

 *

 *
{@code salt} : In cryptography, a salt is random data that is used as an additional input to a one-way function that "hashes" a password or passphrase.

 *
{@code encryptedDataBase64} : Contains encrypted JSON {@code (roleListing, ticketExpiration and sessionKeyBase64)} in Base64 format.

 *
{@code encryptedDataHmacBase64} : HMAC for the {@code encryptedDataBase64} in Base64 format.

 *
{@code ivBase64} : Session Initialization Vector in Base64 format.

 *
{@code seedBase64} : SEED in Base64 format.

 *
{@code serviceTicketBase64} : Service Ticket in Base64 format.

 *

 *
 *

 *     The process of decrypting {@code encryptedDataBase64}, is as follows;

 *         1. Decode all the Base64 fields to binary.
 *         2. Perform a SHA-256 on the clear password followed by the {@code salt}.
Example: {@code byte[] passwordSha256 = sha256(passwordParam.concat(saltParam).getBytes());}
 *         3. Perform a SHA-256 on the step 2 result data and {@code seedBase64}.
{@code byte[] derivedKey = sha256(ArrayUtils.addAll(passwordSha256, seedParam));}
 *         4. Perform a decrypt, using the step 3 result as the key, and provided {@code ivBase64} in binary format.
 *         5. The result of the decrypt must now be used to parse as a JSON object ({@code AuthEncryptedData}). Which will house {@code roleListing}, {@code ticketExpiration} and {@code sessionKeyBase64}.
 *

 *
 *
 *
2.2 Issue Service Token

 *
 * 2.1.1. Send a {@code AppRequestToken} JSON request {@code POST} to;

 *     {@code https://<server:port>/fluid-ws/v1/user/issue_token}

 *         to be issued a service ticket.

 *
 * Example of {@code AppRequestToken} request JSON;

 *
 *  {

 *           "encryptedDataBase64" : "IeWj7pcXhLXf2lEFtRVU",

 *           "encryptedDataHmacBase64", "SdAU3W/V"

 *           "ivBase64", "cCWtbV+K2J4peTNx/7EZ5w=="

 *           "seedBase64", "BRJyFMvUIm/FZq6VxaIzYC9nYnZCZmWEuBARuUQ5JTw="

 *           "serviceTicket", "of6torc"

 *  }

 *
 *
 *

 *
 * Breakdown of {@code AppRequestToken};
 *

 *

 *
{@code encryptedDataBase64} : Encrypted data in Base64 format.
A newly random generated IV will be used.
The {@code sessionKeyBase64} from {@code AuthEncryptedData} will be used as a encryption key.

 *
{@code encryptedDataHmacBase64} : HMAC for encrypted data. Consists of newly generated SEED (each of the bytes poisoned by XOR 222) and derived key.
 Example; {@code byte[] derivedKey = sha256(ArrayUtils.addAll(keyParam, poisonedSeed));}.

 *
{@code ivBase64} : A newly random generated IV.

 *
{@code seedBase64} : A newly random generated SEED.

 *
{@code serviceTicket} : The serviceTicket from {@code AuthResponse}.

 *

 *
 *

 *
 * The response from the Issue Token request will return a JSON object with a {@code serviceTicket}.
 * Each of the API Web Services will require the service ticket to perform functions.
 *
 * @author jasonbruwer
 * @since v1.0
 *
 * @see JSONObject
 * @see com.fluidbpm.program.api.vo.ws.WS.Path.Auth0
 * @see com.fluidbpm.program.api.vo.user.User
 */
public class LoginClient extends ABaseClientWS {

    /**
     * Constructor which sets the login URL.
     *
     * @param urlParam The login URL.
     */
    public LoginClient(String urlParam) {
        super(urlParam);
    }

    /**
     * Performs the necessary login actions against Fluid.
     *
     * Please note that the actual password never travels over the wire / connection.
     *
     * @param usernameParam The users Username.
     * @param passwordParam The users password.
     * @return Session token.
     *
     * @see AppRequestToken
     */
    public AppRequestToken login(
            String usernameParam, String passwordParam) {

        //Default login is for 9 hours.
        return this.login(usernameParam, passwordParam, TimeUnit.HOURS.toSeconds(9));
    }

    /**
     * Performs the necessary login actions against Fluid.
     *
     * Please note that the actual password never travels over the wire / connection.
     *
     * @param usernameParam The users Username.
     * @param passwordParam The users password.
     * @param sessionLifespanSecondsParam The requested duration of the session in seconds.
     * @return Session token.
     *
     * @see AppRequestToken
     */
    public AppRequestToken login(
            String usernameParam, String passwordParam, Long sessionLifespanSecondsParam) {
        if (this.isEmpty(usernameParam) || this.isEmpty(passwordParam)) {
            throw new FluidClientException(
                    "Username and Password required.",
                    FluidClientException.ErrorCode.FIELD_VALIDATE);
        }

        AuthRequest authRequest = new AuthRequest();

        authRequest.setUsername(usernameParam);
        authRequest.setLifetime(sessionLifespanSecondsParam);

        //Init the session...
        //Init the session to get the salt...
        AuthResponse authResponse;
        try {
            authResponse = new AuthResponse(
                    this.postJson(
                            true,
                            authRequest,
                            WS.Path.User.Version1.userInitSession()));
        }
        //JSON format problem...
        catch (JSONException jsonException) {
            throw new FluidClientException(
                    jsonException.getMessage(),jsonException,FluidClientException.ErrorCode.JSON_PARSING);
        }

        AuthEncryptedData authEncData =
                this.initializeSession(passwordParam, authResponse);

        //Issue the token...
        AppRequestToken appReqToken = this.issueAppRequestToken(
                authResponse.getServiceTicketBase64(),
                usernameParam, authEncData);

        appReqToken.setRoleString(authEncData.getRoleListing());
        appReqToken.setSalt(authResponse.getSalt());

        return appReqToken;
    }

    /**
     * Performs HMAC and encryption to initialize the session.
     *
     * @param passwordParam The user password in the clear.
     * @param authResponseParam Response of the initial authentication request (handshake).
     * @return Authenticated encrypted data.
     */
    private AuthEncryptedData initializeSession(
            String passwordParam,
            AuthResponse authResponseParam) {

        //IV...
        byte[] ivBytes = UtilGlobal.decodeBase64(
                authResponseParam.getIvBase64());

        //Seed...
        byte[] seedBytes = UtilGlobal.decodeBase64(
                authResponseParam.getSeedBase64());

        //Encrypted Data...
        byte[] encryptedData = UtilGlobal.decodeBase64(
                authResponseParam.getEncryptedDataBase64());

        //HMac from Response...
        byte[] hMacFromResponse = UtilGlobal.decodeBase64(
                authResponseParam.getEncryptedDataHmacBase64());

        //Local HMac...
        byte[] localGeneratedHMac = AES256Local.generateLocalHMAC(
                encryptedData, passwordParam, authResponseParam.getSalt(), seedBytes);

        //Password mismatch...
        if (!Arrays.equals(hMacFromResponse, localGeneratedHMac)) {
            throw new FluidClientException(
                    "Login attempt failure.",
                    FluidClientException.ErrorCode.LOGIN_FAILURE);
        }

        //Decrypted Initialization Data...
        byte[] decryptedEncryptedData =
                AES256Local.decryptInitPacket(encryptedData,
                        passwordParam,
                        authResponseParam.getSalt(),
                        ivBytes,
                        seedBytes);

        try {

            JSONObject jsonObj = new JSONObject(new String(decryptedEncryptedData));

            return new AuthEncryptedData(jsonObj);
        }
        catch (JSONException jsonExcept) {

            throw new FluidClientException(jsonExcept.getMessage(),
                    FluidClientException.ErrorCode.JSON_PARSING);
        }
    }

    /**
     * Issue a new {@code AppRequestToken} from provided params.
     *
     * @param serviceTicketBase64Param The service ticket from authentication.
     * @param usernameParam The users username.
     * @param authEncryptDataParam The encrypted packet.
     * @return Request Token.
     *
     * @see AppRequestToken
     */
    private AppRequestToken issueAppRequestToken(
            String serviceTicketBase64Param,
            String usernameParam,
            AuthEncryptedData authEncryptDataParam)
    {
        byte[] iv = AES256Local.generateRandom(AES256Local.IV_SIZE_BYTES);
        byte[] seed = AES256Local.generateRandom(AES256Local.SEED_SIZE_BYTES);

        byte[] sessionKey = UtilGlobal.decodeBase64(
                authEncryptDataParam.getSessionKeyBase64());

        byte[] dataToEncrypt = usernameParam.getBytes();

        byte[] encryptedData = AES256Local.encrypt(
                sessionKey,
                dataToEncrypt,
                iv);

        byte[] encryptedDataHMac =
                AES256Local.generateLocalHMACForReqToken(encryptedData, sessionKey, seed);

        AppRequestToken requestToServer = new AppRequestToken();

        requestToServer.setEncryptedDataBase64(UtilGlobal.encodeBase64(encryptedData));
        requestToServer.setEncryptedDataHmacBase64(UtilGlobal.encodeBase64(encryptedDataHMac));
        requestToServer.setIvBase64(UtilGlobal.encodeBase64(iv));
        requestToServer.setSeedBase64(UtilGlobal.encodeBase64(seed));
        requestToServer.setServiceTicket(serviceTicketBase64Param);

        try {
            return new AppRequestToken(
                    this.postJson(requestToServer, WS.Path.User.Version1.userIssueToken()));
        }
        //
        catch (JSONException jsonExcept) {
            throw new FluidClientException(jsonExcept.getMessage(),
                    jsonExcept, FluidClientException.ErrorCode.JSON_PARSING);
        }
    }
}


/*
 * Koekiebox CONFIDENTIAL
 *
 * [2012] - [2017] Koekiebox (Pty) Ltd
 * All Rights Reserved.
 *
 * NOTICE: All information contained herein is, and remains the property
 * of Koekiebox and its suppliers, if any. The intellectual and
 * technical concepts contained herein are proprietary to Koekiebox
 * and its suppliers and may be covered by South African and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material is strictly
 * forbidden unless prior written permission is obtained from Koekiebox.
 */

package com.fluidbpm.program.api.vo.flow;

import java.util.ArrayList;
import java.util.List;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.fluidbpm.program.api.vo.ABaseFluidJSONObject;
import com.fluidbpm.program.api.vo.item.FluidItem;
import com.fluidbpm.program.api.vo.mail.MailMessage;
import com.fluidbpm.program.api.vo.user.User;

/**
 *

 *     Container POJO used to send back result from
 *     {@code FlowItemExecutePacket}.
 *

 *
 * @author jasonbruwer
 * @since v1.0
 * @see FlowItemExecutePacket
 */
public class FlowItemExecuteResult extends ABaseFluidJSONObject {

    public static final long serialVersionUID = 1L;

    private FlowStepRule flowStepRule;
    private FluidItem fluidItem;
    private List<FluidItem> fluidItems;

    private String assignmentRuleValue;
    private String statementResultAsString;

    //regards to schedule and flow-program...
    //flow-program...
    private String executePerFluidItemQuery;
    private Boolean progressToNextPhase;

    //schedule-program...
    private String fluidItemQuery;
    private String executionResult;
    private List<User> executeUsers;
    private List<MailMessage> mailMessagesToSend;

    /**
     * The JSON mapping for the {@code FlowItemExecuteResult} object.
     */
    public static class JSONMapping
    {
        public static final String FLOW_STEP_RULE = "flowStepRule";
        public static final String FLUID_ITEM = "fluidItem";
        public static final String FLUID_ITEMS = "fluidItems";
        public static final String ASSIGNMENT_RULE_VALUE = "assignmentRuleValue";
        public static final String STATEMENT_RESULT_AS_STRING = "statementResultAsString";

        //flow programs...
        public static final String EXECUTE_PER_FLUID_ITEM_QUERY = "executePerFluidItemQuery";
        public static final String PROGRESS_TO_NEXT_PHASE = "progressToNextPhase";

        //schedule programs...
        public static final String FLUID_ITEM_QUERY = "fluidItemQuery";
        public static final String EXECUTION_RESULT = "executionResult";
        public static final String EXECUTE_USERS = "executeUsers";
        public static final String MAIL_MESSAGES_TO_SEND = "mailMessagesToSend";
    }

    /**
     * Default constructor.
     */
    public FlowItemExecuteResult() {
        super();
    }

    /**
     * Populates local variables with {@code jsonObjectParam}.
     *
     * @param jsonObjectParam The JSON Object.
     */
    public FlowItemExecuteResult(JSONObject jsonObjectParam) {
        super(jsonObjectParam);

        if(this.jsonObject == null)
        {
            return;
        }

        //Fluid Item...
        if (!this.jsonObject.isNull(JSONMapping.FLUID_ITEM)) {

            this.setFluidItem(new FluidItem(this.jsonObject.getJSONObject(
                    JSONMapping.FLUID_ITEM)));
        }

        //Flow Step Rule...
        if (!this.jsonObject.isNull(JSONMapping.FLOW_STEP_RULE)) {

            this.setFlowStepRule(new FlowStepRule(
                    this.jsonObject.getJSONObject(JSONMapping.FLOW_STEP_RULE)));
        }

        //Assignment Rule Value...
        if (!this.jsonObject.isNull(JSONMapping.ASSIGNMENT_RULE_VALUE)) {

            this.setAssignmentRuleValue(
                    this.jsonObject.getString(JSONMapping.ASSIGNMENT_RULE_VALUE));
        }

        //Statement Result As String...
        if (!this.jsonObject.isNull(JSONMapping.STATEMENT_RESULT_AS_STRING)) {

            this.setStatementResultAsString(
                    this.jsonObject.getString(JSONMapping.STATEMENT_RESULT_AS_STRING));
        }

        //Execute per Fluid Item Query...
        if (!this.jsonObject.isNull(
                JSONMapping.EXECUTE_PER_FLUID_ITEM_QUERY)) {

            this.setExecutePerFluidItemQuery(this.jsonObject.getString(
                            JSONMapping.EXECUTE_PER_FLUID_ITEM_QUERY));
        }

        //Progress to next phase...
        if (!this.jsonObject.isNull(
                JSONMapping.PROGRESS_TO_NEXT_PHASE)) {

            this.setProgressToNextPhase(this.jsonObject.getBoolean(
                    JSONMapping.PROGRESS_TO_NEXT_PHASE));
        }

        //Fluid Item Query...
        if (!this.jsonObject.isNull(JSONMapping.FLUID_ITEM_QUERY)) {

            this.setFluidItemQuery(this.jsonObject.getString(
                    JSONMapping.FLUID_ITEM_QUERY));
        }

        //Execution Result...
        if (!this.jsonObject.isNull(JSONMapping.EXECUTION_RESULT)) {

            this.setExecutionResult(this.jsonObject.getString(
                    JSONMapping.EXECUTION_RESULT));
        }

        //Fluid Items...
        if (!this.jsonObject.isNull(JSONMapping.FLUID_ITEMS)) {

            JSONArray fluidItemsArr = this.jsonObject.getJSONArray(JSONMapping.FLUID_ITEMS);

            List<FluidItem> listOfItems = new ArrayList();
            for(int index = 0;index < fluidItemsArr.length();index++)
            {
                listOfItems.add(new FluidItem(fluidItemsArr.getJSONObject(index)));
            }

            this.setFluidItems(listOfItems);
        }

        //Execute Users...
        if (!this.jsonObject.isNull(JSONMapping.EXECUTE_USERS)) {

            JSONArray executeUsersArr =
                    this.jsonObject.getJSONArray(JSONMapping.EXECUTE_USERS);

            List<User> listOfItems = new ArrayList();
            for(int index = 0;index < executeUsersArr.length();index++)
            {
                listOfItems.add(new User(executeUsersArr.getJSONObject(index)));
            }

            this.setExecuteUsers(listOfItems);
        }

        //Mail Messages to send...
        if (!this.jsonObject.isNull(JSONMapping.MAIL_MESSAGES_TO_SEND)) {

            JSONArray mailMessagesToSendArr =
                    this.jsonObject.getJSONArray(JSONMapping.MAIL_MESSAGES_TO_SEND);

            List<MailMessage> listOfItems = new ArrayList();
            for(int index = 0;index < mailMessagesToSendArr.length();index++)
            {
                listOfItems.add(new MailMessage(
                        mailMessagesToSendArr.getJSONObject(index)));
            }

            this.setMailMessagesToSend(listOfItems);
        }
    }

    /**
     * Conversion to {@code JSONObject} from Java Object.
     *
     * @return {@code JSONObject} representation of {@code FlowItemExecuteResult}
     * @throws JSONException If there is a problem with the JSON Body.
     *
     * @see ABaseFluidJSONObject#toJsonObject()
     */
    @Override
    public JSONObject toJsonObject() throws JSONException
    {
        JSONObject returnVal = super.toJsonObject();

        //Fluid Item...
        if(this.getFluidItem() != null)
        {
            returnVal.put(JSONMapping.FLUID_ITEM,
                    this.getFluidItem().toJsonObject());
        }

        //Flow Step Rule...
        if(this.getFlowStepRule() != null)
        {
            returnVal.put(JSONMapping.FLOW_STEP_RULE,
                    this.getFlowStepRule().toJsonObject());
        }

        //Assignment Rule...
        if(this.getAssignmentRuleValue() != null)
        {
            returnVal.put(JSONMapping.ASSIGNMENT_RULE_VALUE,
                    this.getAssignmentRuleValue());
        }

        //Statement Result as String...
        if(this.getStatementResultAsString() != null)
        {
            returnVal.put(JSONMapping.STATEMENT_RESULT_AS_STRING,
                    this.getStatementResultAsString());
        }

        //Execute per Fluid Item Query...
        if(this.getExecutePerFluidItemQuery() != null)
        {
            returnVal.put(JSONMapping.EXECUTE_PER_FLUID_ITEM_QUERY,
                    this.getExecutePerFluidItemQuery());
        }

        //Fluid Item Query...
        if(this.getFluidItemQuery() != null)
        {
            returnVal.put(JSONMapping.FLUID_ITEM_QUERY,
                    this.getFluidItemQuery());
        }

        //Execution Result...
        if(this.getExecutionResult() != null)
        {
            returnVal.put(JSONMapping.EXECUTION_RESULT,
                    this.getExecutionResult());
        }

        //Progress to next phase...
        if(this.getProgressToNextPhase() != null)
        {
            returnVal.put(JSONMapping.PROGRESS_TO_NEXT_PHASE,
                    this.getProgressToNextPhase());
        }

        //Fluid Items...
        if(this.getFluidItems() != null && !this.getFluidItems().isEmpty())
        {
            JSONArray jsonArray = new JSONArray();

            for(FluidItem item : this.getFluidItems())
            {
                jsonArray.put(item.toJsonObject());
            }

            returnVal.put(JSONMapping.FLUID_ITEMS, jsonArray);
        }

        //Execute Users...
        if(this.getExecuteUsers() != null && !this.getExecuteUsers().isEmpty())
        {
            JSONArray jsonArray = new JSONArray();

            for(User item : this.getExecuteUsers())
            {
                jsonArray.put(item.toJsonObject());
            }

            returnVal.put(JSONMapping.EXECUTE_USERS, jsonArray);
        }

        //Mail Messages To Send...
        if(this.getMailMessagesToSend() != null &&
                !this.getMailMessagesToSend().isEmpty())
        {
            JSONArray jsonArray = new JSONArray();

            for(MailMessage item : this.getMailMessagesToSend())
            {
                jsonArray.put(item.toJsonObject());
            }

            returnVal.put(JSONMapping.MAIL_MESSAGES_TO_SEND, jsonArray);
        }

        return returnVal;
    }

    /**
     * Gets the rule to execute.
     *
     * @return Rule to execute.
     *
     * @see FlowStepRule
     */
    public FlowStepRule getFlowStepRule() {
        return this.flowStepRule;
    }

    /**
     * Sets the rule to execute.
     *
     * @param flowStepRuleParam Rule to execute.
     *
     * @see FlowStepRule
     */
    public void setFlowStepRule(FlowStepRule flowStepRuleParam) {
        this.flowStepRule = flowStepRuleParam;
    }

    /**
     * Gets the Fluid item.
     *
     * @return Fluid item.
     *
     * @see FluidItem
     */
    public FluidItem getFluidItem() {
        return this.fluidItem;
    }

    /**
     * Sets the Fluid item.
     *
     * @param fluidItemParam Fluid item.
     *
     * @see FluidItem
     */
    public void setFluidItem(FluidItem fluidItemParam) {
        this.fluidItem = fluidItemParam;
    }

    /**
     * Gets the Fluid items.
     *
     * @return Fluid items.
     *
     * @see FluidItem
     */
    public List<FluidItem> getFluidItems() {
        return this.fluidItems;
    }

    /**
     * Sets the Fluid items.
     *
     * @param fluidItemsParam Fluid items.
     *
     * @see FluidItem
     */
    public void setFluidItems(List<FluidItem> fluidItemsParam) {
        this.fluidItems = fluidItemsParam;
    }

    /**
     * Gets the new Field assignment value.
     *
     * @return New Assignment value.
     */
    public String getAssignmentRuleValue() {
        return this.assignmentRuleValue;
    }

    /**
     * Sets the new Field assignment value.
     *
     * @param assignmentRuleValueParam New Assignment value.
     */
    public void setAssignmentRuleValue(String assignmentRuleValueParam) {
        this.assignmentRuleValue = assignmentRuleValueParam;
    }

    /**
     * Gets the Statement result value.
     *
     * @return Statement result as Text.
     */
    public String getStatementResultAsString() {
        return this.statementResultAsString;
    }

    /**
     * Sets the Statement result value.
     *
     * @param statementResultAsStringParam Statement result as Text.
     */
    public void setStatementResultAsString(String statementResultAsStringParam) {
        this.statementResultAsString = statementResultAsStringParam;
    }

    /**
     * Gets the ExecutePerFluidItem query.
     *
     * @return Execute Per Fluid Item Query Text.
     */
    public String getExecutePerFluidItemQuery() {
        return this.executePerFluidItemQuery;
    }

    /**
     * Sets the ExecutePerFluidItem query.
     *
     * @param executePerFluidItemQueryParam Execute Per Fluid Item Query Text.
     */
    public void setExecutePerFluidItemQuery(
            String executePerFluidItemQueryParam) {
        this.executePerFluidItemQuery = executePerFluidItemQueryParam;
    }

    /**
     * Gets the flag to progress to the next phase.
     *
     * @return Progress to the next phase.
     */
    public Boolean getProgressToNextPhase() {
        return this.progressToNextPhase;
    }

    /**
     * Sets the flag to progress to the next phase.
     *
     * @param progressToNextPhaseParam Progress to the next phase.
     */
    public void setProgressToNextPhase(Boolean progressToNextPhaseParam) {
        this.progressToNextPhase = progressToNextPhaseParam;
    }

    /**
     * Gets the fluid item query.
     *
     * @return Text version of query to execute.
     */
    public String getFluidItemQuery() {
        return this.fluidItemQuery;
    }

    /**
     * Sets the fluid item query.
     *
     * @param fluidItemQueryParam Text version of query to execute.
     */
    public void setFluidItemQuery(String fluidItemQueryParam) {
        this.fluidItemQuery = fluidItemQueryParam;
    }

    /**
     * Gets the final execution result.
     * The result will form part of the schedule output message.
     *
     * @return Text result.
     */
    public String getExecutionResult() {
        return this.executionResult;
    }

    /**
     * Sets the final execution result.
     * The result will form part of the schedule output message.
     *
     * @param executionResultParam Text result.
     */
    public void setExecutionResult(String executionResultParam) {
        this.executionResult = executionResultParam;
    }

    /**
     * Gets the {@link User}s to update as part of the schedule program to update.
     *
     * @return List of users.
     *
     * @see User
     */
    public List<User> getExecuteUsers() {
        return this.executeUsers;
    }

    /**
     * Sets the {@link User}s to update as part of the schedule program to update.
     *
     * @param executeUsersParam List of users.
     *
     * @see User
     */
    public void setExecuteUsers(List<User> executeUsersParam) {
        this.executeUsers = executeUsersParam;
    }

    /**
     * Gets the {@link MailMessage}s to send.
     *
     * @return Mail messages to send as part of Schedule program.
     *
     * @see MailMessage
     */
    public List<MailMessage> getMailMessagesToSend() {
        return this.mailMessagesToSend;
    }

    /**
     * Sets the {@link MailMessage}s to send.
     *
     * @param mailMessagesToSendParam Mail messages to send as part of Schedule program.
     *
     * @see MailMessage
     */
    public void setMailMessagesToSend(List<MailMessage> mailMessagesToSendParam) {
        this.mailMessagesToSend = mailMessagesToSendParam;
    }
}


/*
 * Koekiebox CONFIDENTIAL
 *
 * [2012] - [2017] Koekiebox (Pty) Ltd
 * All Rights Reserved.
 *
 * NOTICE: All information contained herein is, and remains the property
 * of Koekiebox and its suppliers, if any. The intellectual and
 * technical concepts contained herein are proprietary to Koekiebox
 * and its suppliers and may be covered by South African and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material is strictly
 * forbidden unless prior written permission is obtained from Koekiebox.
 */

package com.fluidbpm.program.api.vo.field;

import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.annotation.XmlTransient;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.fluidbpm.program.api.util.UtilGlobal;
import com.fluidbpm.program.api.vo.ABaseFluidJSONObject;

/**
 * <p>
 *     Represents a {@code MultiChoice} value at any level (Form, Route, User and Global).
 * </p>
 *
 * @author jasonbruwer
 * @since v1.0
 * @version v1.8
 *
 * @see Field
 * @see Field#setFieldValue(Object)
 */
public class MultiChoice extends ABaseFluidJSONObject {

	public static final long serialVersionUID = 1L;

	private List<String> availableMultiChoices;
	private String availableMultiChoicesCombined;

	private List<String> selectedMultiChoices;
	private String selectedMultiChoicesCombined;

	/**
	 * The JSON mapping for the {@code MultiChoice} object.
	 */
	public static class JSONMapping {
		public static final String AVAILABLE_MULTI_CHOICES = "availableMultiChoices";
		public static final String SELECTED_MULTI_CHOICES = "selectedMultiChoices";

		public static final String AVAILABLE_CHOICES = "availableChoices";
		public static final String SELECTED_CHOICES = "selectedChoices";

		public static final String AVAILABLE_CHOICES_COMBINED = "availableChoicesCombined";
		public static final String SELECTED_CHOICES_COMBINED = "selectedChoicesCombined";

		//For Payara mapping of [type] and [value]...
		public static final String TYPE = "type";
		public static final String TYPE_STRING = "string";

		public static final String VALUE = "value";
	}

	/**
	 * Default constructor.
	 */
	public MultiChoice() {
		super();
	}

	/**
	 * Sets the Selected MultiChoices for when multiple selections are supported.
	 *
	 * @param selectedMultiChoicesParam List of Selected {@code MultiChoices}s.
	 */
	public MultiChoice(List<String> selectedMultiChoicesParam) {
		this.selectedMultiChoices = selectedMultiChoicesParam;
	}

	/**
	 * Sets the Selected and Available MultiChoices for when multiple selections are supported.
	 *
	 * @param selectedMultiChoicesParam List of Selected {@code MultiChoices}s.
	 * @param availableMultiChoicesParam List of Available {@code MultiChoices}s.
	 */
	public MultiChoice(
			List<String> selectedMultiChoicesParam,
			List<String> availableMultiChoicesParam) {
		this.selectedMultiChoices = selectedMultiChoicesParam;
		this.availableMultiChoices = availableMultiChoicesParam;
	}

	/**
	 * Sets the Selected MultiChoices for when multiple selections are supported.
	 *
	 * @param selectedMultiChoiceValueParam Selected {@code MultiChoices} {@code String} value.
	 */
	public MultiChoice(String selectedMultiChoiceValueParam) {
		this.selectedMultiChoices = new ArrayList();
		if(selectedMultiChoiceValueParam != null) {
			this.selectedMultiChoices.add(selectedMultiChoiceValueParam);
		}
	}

	/**
	 * Sets the Selected MultiChoices for when multiple selections are supported.
	 *
	 * @param selectedMultiChoiceValuesParam Selected {@code MultiChoices} {@code String[]} value.
	 */
	public MultiChoice(String ... selectedMultiChoiceValuesParam) {
		this.selectedMultiChoices = new ArrayList();
		if(selectedMultiChoiceValuesParam != null) {
			for(String str : selectedMultiChoiceValuesParam) {
				this.selectedMultiChoices.add(str);
			}
		}
	}

	/**
	 * Populates local variables with {@code jsonObjectParam}
	 *
	 * @param jsonObjectParam The JSON Object.
	 */
	public MultiChoice(JSONObject jsonObjectParam) {
		super(jsonObjectParam);

		if(this.jsonObject == null) {
			return;
		}

		//Available Multiple Choices...
		if (!this.jsonObject.isNull(JSONMapping.AVAILABLE_MULTI_CHOICES)) {

			JSONArray arrayOfString =
					this.jsonObject.getJSONArray(JSONMapping.AVAILABLE_MULTI_CHOICES);

			this.availableMultiChoices = new ArrayList();

			for(int index = 0;index < arrayOfString.length();index++)
			{
				this.availableMultiChoices.add(arrayOfString.getString(index));
			}
		} else if (!this.jsonObject.isNull(JSONMapping.AVAILABLE_CHOICES)) {

			JSONArray arrayOfString =
					this.jsonObject.getJSONArray(JSONMapping.AVAILABLE_CHOICES);

			this.availableMultiChoices = new ArrayList();

			for(int index = 0;index < arrayOfString.length();index++)
			{
				this.availableMultiChoices.add(arrayOfString.getString(index));
			}
		} else if (!this.jsonObject.isNull(JSONMapping.AVAILABLE_CHOICES_COMBINED)) {

			String combinedAvailChoices =
					this.jsonObject.getString(JSONMapping.AVAILABLE_CHOICES_COMBINED);

			if(combinedAvailChoices != null && !combinedAvailChoices.isEmpty()) {
				this.availableMultiChoices = new ArrayList();

				String[] pipeSplit = combinedAvailChoices.split(UtilGlobal.REG_EX_PIPE);

				if(pipeSplit != null && pipeSplit.length > 0) {
					for(int index = 0;index < pipeSplit.length;index++) {
						this.availableMultiChoices.add(pipeSplit[index]);
					}
				}
			}
		}

		//Avail - Populate combined...
		if(this.availableMultiChoices != null) {
			this.availableMultiChoicesCombined =
					this.combineStringArrayWith(
							this.availableMultiChoices,
							UtilGlobal.PIPE);
		}

		//Selected Multiple Choices...
		if (!this.jsonObject.isNull(JSONMapping.SELECTED_MULTI_CHOICES)) {

			JSONArray arrayOfString =
					this.jsonObject.getJSONArray(JSONMapping.SELECTED_MULTI_CHOICES);

			this.selectedMultiChoices = new ArrayList();

			for(int index = 0;index < arrayOfString.length();index++)
			{
				this.selectedMultiChoices.add(arrayOfString.getString(index));
			}
		} else if (!this.jsonObject.isNull(JSONMapping.SELECTED_CHOICES)) {

			JSONArray arrayOfString =
					this.jsonObject.getJSONArray(JSONMapping.SELECTED_CHOICES);

			this.selectedMultiChoices = new ArrayList();

			for(int index = 0;index < arrayOfString.length();index++) {
				this.selectedMultiChoices.add(arrayOfString.getString(index));
			}
		} else if (!this.jsonObject.isNull(JSONMapping.SELECTED_CHOICES_COMBINED)) {

			String combinedSelectedChoices =
					this.jsonObject.getString(JSONMapping.SELECTED_CHOICES_COMBINED);

			if(combinedSelectedChoices != null && !combinedSelectedChoices.isEmpty()) {
				this.selectedMultiChoices = new ArrayList();

				String[] pipeSplit = combinedSelectedChoices.split(UtilGlobal.REG_EX_PIPE);

				if(pipeSplit != null && pipeSplit.length > 0) {
					for(int index = 0;index < pipeSplit.length;index++) {
						this.selectedMultiChoices.add(pipeSplit[index]);
					}
				}
			}
		}

		//Selected - Populate combined...
		if(this.selectedMultiChoices != null) {
			this.selectedMultiChoicesCombined =
					this.combineStringArrayWith(
							this.selectedMultiChoices,
							UtilGlobal.PIPE);
		}
	}

	/**
	 * Conversion to {@code JSONObject} from Java Object.
	 *
	 * @return {@code JSONObject} representation of {@code MultiChoice}.
	 * @throws JSONException If there is a problem with the JSON Body.
	 *
	 * @see ABaseFluidJSONObject#toJsonObject()
	 */
	@Override
	public JSONObject toJsonObject() throws JSONException {
		JSONObject returnVal = super.toJsonObject();

		//Available...
		if(this.getAvailableMultiChoices() != null) {
			List<String> availChoices = this.getAvailableMultiChoices();

			returnVal.put(JSONMapping.AVAILABLE_MULTI_CHOICES,
					new JSONArray(availChoices.toArray()));

			returnVal.put(JSONMapping.AVAILABLE_CHOICES,
					new JSONArray(availChoices.toArray()));

			returnVal.put(
					JSONMapping.AVAILABLE_CHOICES_COMBINED,
					this.combineStringArrayWith(
							availChoices,
							UtilGlobal.PIPE));
		}

		//Selected...
		if(this.getSelectedMultiChoices() != null) {
			List<String> selectChoices = this.getSelectedMultiChoices();

			returnVal.put(JSONMapping.SELECTED_MULTI_CHOICES,
					new JSONArray(selectChoices.toArray()));

			returnVal.put(JSONMapping.SELECTED_CHOICES,
					new JSONArray(selectChoices.toArray()));

			returnVal.put(
					JSONMapping.SELECTED_CHOICES_COMBINED,
					this.combineStringArrayWith(
							selectChoices,
							UtilGlobal.PIPE));
		}

		return returnVal;
	}

	/**
	 * Constructs a {@code String} value for the selected MultiChoices.
	 *
	 * @return Comma separated list of Selected MultiChoices.
	 *
	 * @see MultiChoice#getSelectedMultiChoices()
	 */
	@Override
	public String toString() {

		return this.combineStringArrayWith(
				this.getSelectedMultiChoices(),
				UtilGlobal.COMMA_SPACE);
	}

	/**
	 * Gets Available MultiChoices.
	 *
	 * @return {@code List} of available multi choices.
	 */
	public List<String> getAvailableMultiChoices() {
		return this.availableMultiChoices;
	}

	/**
	 * Sets Available MultiChoices.
	 *
	 * @param availableMultiChoices {@code List} of available multi choices.
	 */
	public void setAvailableMultiChoices(List<String> availableMultiChoices) {
		this.availableMultiChoices = availableMultiChoices;
	}

	/**
	 * Gets Selected MultiChoices.
	 *
	 * @return {@code List} of selected multi choices.
	 */
	public List<String> getSelectedMultiChoices() {
		return this.selectedMultiChoices;
	}

	/**
	 * Sets Selected MultiChoices.
	 *
	 * @param selectedMultiChoices {@code List} of selected multi choices.
	 */
	public void setSelectedMultiChoices(List<String> selectedMultiChoices) {
		this.selectedMultiChoices = selectedMultiChoices;
	}

	/**
	 * Gets Selected MultiChoices combined Text.
	 *
	 * @return {@code List} of selected multi choices combined.
	 */
	public String getSelectedMultiChoicesCombined() {
		return this.selectedMultiChoicesCombined;
	}

	/**
	 * Sets Selected MultiChoices combined.
	 *
	 * @param selectedMultiChoicesCombinedParam combined text of multi choices.
	 */
	public void setSelectedMultiChoicesCombined(String selectedMultiChoicesCombinedParam) {
		this.selectedMultiChoicesCombined = selectedMultiChoicesCombinedParam;
	}

	/**
	 * Gets Available MultiChoices combined Text.
	 *
	 * @return {@code List} of available multi choices combined.
	 */
	public String getAvailableMultiChoicesCombined() {
		return this.availableMultiChoicesCombined;
	}

	/**
	 * Sets Available MultiChoices combined.
	 *
	 * @param availableMultiChoicesCombinedParam combined text of multi choices.
	 */
	public void setAvailableMultiChoicesCombined(String availableMultiChoicesCombinedParam) {
		this.availableMultiChoicesCombined = availableMultiChoicesCombinedParam;
	}

	/**
	 * Combine {@code listToCombineParam} into a single {@code String}.
	 *
	 * @param listToCombineParam To combine.
	 * @param separatorCharsParam The char used to separate with.
	 *
	 * @return The combined text value.
	 */
	@XmlTransient
	public String combineStringArrayWith(
			List<String> listToCombineParam,
			String separatorCharsParam) {
		String returnValue = UtilGlobal.EMPTY;

		int lengthOfSepChars = (separatorCharsParam == null) ? 0 :
				separatorCharsParam.length();

		if(listToCombineParam != null && !listToCombineParam.isEmpty()) {
			StringBuffer concatBuffer = new StringBuffer();
			for(String toAdd : listToCombineParam) {
				concatBuffer.append(toAdd);
				concatBuffer.append(separatorCharsParam);
			}

			String concatString = concatBuffer.toString();
			returnValue = concatString.substring(0, concatString.length() - lengthOfSepChars);
		}

		return returnValue;
	}
}


/*
 * Koekiebox CONFIDENTIAL
 *
 * [2012] - [2017] Koekiebox (Pty) Ltd
 * All Rights Reserved.
 *
 * NOTICE: All information contained herein is, and remains the property
 * of Koekiebox and its suppliers, if any. The intellectual and
 * technical concepts contained herein are proprietary to Koekiebox
 * and its suppliers and may be covered by South African and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material is strictly
 * forbidden unless prior written permission is obtained from Koekiebox.
 */

package com.fluidbpm.ws.client.v1.userquery;

import org.json.JSONException;
import org.json.JSONObject;

import com.fluidbpm.program.api.vo.item.FluidItemListing;
import com.fluidbpm.program.api.vo.userquery.UserQuery;
import com.fluidbpm.program.api.vo.userquery.UserQueryListing;
import com.fluidbpm.program.api.vo.ws.WS;
import com.fluidbpm.ws.client.FluidClientException;
import com.fluidbpm.ws.client.v1.ABaseClientWS;

/**
 * Java Web Service Client for {@code UserQuery} related actions.
 *
 * @author jasonbruwer
 * @since v1.0
 *
 * @see JSONObject
 * @see WS.Path.UserQuery
 * @see UserQuery
 */
public class UserQueryClient extends ABaseClientWS {

	/**
	 * Constructor that sets the Service Ticket from authentication.
	 *
	 * @param endpointBaseUrlParam URL to base endpoint.
	 * @param serviceTicketParam The Server issued Service Ticket.
	 */
	public UserQueryClient(
			String endpointBaseUrlParam,
			String serviceTicketParam) {
		super(endpointBaseUrlParam);

		this.setServiceTicket(serviceTicketParam);
	}

	/**
	 * Creates a new {@code UserQuery}.
	 *
	 * @param userQueryParam The {@code UserQuery} to create.
	 * @return The Created User Query.
	 *
	 * @see com.fluidbpm.program.api.vo.userquery.UserQuery
	 */
	public UserQuery createUserQuery(UserQuery userQueryParam)
	{
		if(userQueryParam != null && this.serviceTicket != null)
		{
			userQueryParam.setServiceTicket(this.serviceTicket);
		}

		return new UserQuery(this.putJson(
				userQueryParam,
				WS.Path.UserQuery.Version1.userQueryCreate()));
	}

	/**
	 * Updates an existing {@code UserQuery}.
	 *
	 * @param userQueryParam The {@code UserQuery} to update.
	 * @return The Updated UserQuery.
	 *
	 * @see UserQuery
	 */
	public UserQuery updateUserQuery(UserQuery userQueryParam) {
		if(userQueryParam != null && this.serviceTicket != null) {
			userQueryParam.setServiceTicket(this.serviceTicket);
		}

		return new UserQuery(this.postJson(
				userQueryParam,
				WS.Path.UserQuery.Version1.userQueryUpdate()));
	}

	/**
	 * Deletes the {@code UserQuery} provided.
	 * Id must be set on the {@code UserQuery}.
	 *
	 * @param userQueryToDeleteParam The UserQuery to Delete.
	 * @return The deleted UserQuery.
	 */
	public UserQuery deleteUserQuery(
			UserQuery userQueryToDeleteParam
	) {
		if(userQueryToDeleteParam != null && this.serviceTicket != null) {
			userQueryToDeleteParam.setServiceTicket(this.serviceTicket);
		}

		return new UserQuery(this.postJson(userQueryToDeleteParam,
				WS.Path.UserQuery.Version1.userQueryDelete()));
	}

	/**
	 * Deletes the {@code UserQuery} provided.
	 * Id must be set on the {@code UserQuery}.
	 *
	 * @param userQueryToDeleteParam The UserQuery to Delete.
	 * @param forcefullyDeleteParam Delete the UserQuery forcefully.
	 * @return The deleted UserQuery.
	 */
	public UserQuery deleteUserQuery(
			UserQuery userQueryToDeleteParam,
			boolean forcefullyDeleteParam
	) {
		if(userQueryToDeleteParam != null && this.serviceTicket != null) {
			userQueryToDeleteParam.setServiceTicket(this.serviceTicket);
		}

		return new UserQuery(this.postJson(userQueryToDeleteParam,
				WS.Path.UserQuery.Version1.userQueryDelete(forcefullyDeleteParam)));
	}

	/**
	 * Retrieves User Query information for the provided {@code userQueryIdParam}.
	 *
	 * @param userQueryIdParam The ID of the {@code UserQuery} to retrieve info for.
	 *
	 * @return UserQuery information.
	 *
	 * @see UserQuery
	 */
	public UserQuery getUserQueryById(Long userQueryIdParam) {
		UserQuery userQueryToGetInfoFor = new UserQuery();
		userQueryToGetInfoFor.setId(userQueryIdParam);

		if(this.serviceTicket != null) {
			userQueryToGetInfoFor.setServiceTicket(this.serviceTicket);
		}

		try {
			return new UserQuery(this.postJson(
					userQueryToGetInfoFor, WS.Path.UserQuery.Version1.getById()));
		} catch (JSONException jsonExcept) {
			throw new FluidClientException(jsonExcept.getMessage(),
					FluidClientException.ErrorCode.JSON_PARSING);
		}
	}

	/**
	 * Retrieves all user query information.
	 *
	 * @return UserQuery information.
	 *
	 * @see UserQueryListing
	 */
	public UserQueryListing getAllUserQueries() {
		UserQuery userQueryToGetInfoFor = new UserQuery();

		if(this.serviceTicket != null) {
			userQueryToGetInfoFor.setServiceTicket(this.serviceTicket);
		}

		try {
			return new UserQueryListing(this.postJson(
					userQueryToGetInfoFor, WS.Path.UserQuery.Version1.getAllUserQueries()));
		}
		//
		catch (JSONException jsonExcept) {
			throw new FluidClientException(jsonExcept.getMessage(),
					FluidClientException.ErrorCode.JSON_PARSING);
		}
	}

	/**
	 * Retrieves all user queries for logged in {@code User}.
	 *
	 * @return UserQuery information.
	 *
	 * @see UserQueryListing
	 */
	public UserQueryListing getAllUserQueriesByLoggedInUser() {
		UserQuery userQueryToGetInfoFor = new UserQuery();

		if(this.serviceTicket != null) {
			userQueryToGetInfoFor.setServiceTicket(this.serviceTicket);
		}

		try {
			return new UserQueryListing(this.postJson(
					userQueryToGetInfoFor, WS.Path.UserQuery.Version1.getAllUserQueriesByLoggedInUser()));
		} catch (JSONException jsonExcept) {
			throw new FluidClientException(jsonExcept.getMessage(),
					FluidClientException.ErrorCode.JSON_PARSING);
		}
	}

	/**
	 * Executes the {@code UserQuery} {@code queryToExecuteParam}
	 * and returns the result information.
	 *
	 * @param queryToExecuteParam The UserQuery to execute.
	 *
	 * @return The UserQuery result.
	 *
	 * @see FluidItemListing
	 */
	public FluidItemListing executeUserQuery(UserQuery queryToExecuteParam) {
		return this.executeUserQuery(queryToExecuteParam, true);
	}

	/**
	 * Executes the {@code UserQuery} {@code queryToExecuteParam}
	 * and returns the result information.
	 *
	 * @param queryToExecuteParam The UserQuery to execute.
	 * @param populateAncestorIdParam - Whether the ancestor id should be populated (when applicable).
	 *
	 *
	 * @return The UserQuery result.
	 *
	 * @see FluidItemListing
	 */
	public FluidItemListing executeUserQuery(
			UserQuery queryToExecuteParam,
			boolean populateAncestorIdParam
	) {
		return this.executeUserQuery(
				queryToExecuteParam,
				populateAncestorIdParam,
				-1,
				-1,
				false);
	}

	/**
	 * Executes the {@code UserQuery} {@code queryToExecuteParam}
	 * and returns the result information.
	 *
	 * @param queryToExecuteParam The UserQuery to execute.
	 * @param populateAncestorIdParam - Whether the ancestor id should be populated (when applicable).
	 * @param queryLimitParam The query limit.
	 * @param offsetParam The query offset.
	 *
	 * @return The UserQuery result.
	 *
	 * @see FluidItemListing
	 */
	public FluidItemListing executeUserQuery(
			UserQuery queryToExecuteParam,
			boolean populateAncestorIdParam,
			int queryLimitParam,
			int offsetParam
	) {
		return this.executeUserQuery(
				queryToExecuteParam,
				populateAncestorIdParam,
				queryLimitParam,
				offsetParam,
				false);
	}

	/**
	 * Executes the {@code UserQuery} {@code queryToExecuteParam}
	 * and returns the result information.
	 *
	 * @param queryToExecuteParam The UserQuery to execute.
	 * @param populateAncestorIdParam - Whether the ancestor id should be populated (when applicable).
	 * @param queryLimitParam The query limit.
	 * @param offsetParam The query offset.
	 * @param forceUseDatabaseParam Force to use underlying database.
	 *
	 * @return The UserQuery result.
	 *
	 * @see FluidItemListing
	 */
	public FluidItemListing executeUserQuery(
			UserQuery queryToExecuteParam,
			boolean populateAncestorIdParam,
			int queryLimitParam,
			int offsetParam,
			boolean forceUseDatabaseParam
	) {
		if(this.serviceTicket != null && queryToExecuteParam != null) {
			queryToExecuteParam.setServiceTicket(this.serviceTicket);
		}

		try {
			return new FluidItemListing(this.postJson(
					queryToExecuteParam, WS.Path.UserQuery.Version1.executeUserQuery(
							populateAncestorIdParam,
							forceUseDatabaseParam,
							queryLimitParam,
							offsetParam)));
		} catch (JSONException jsonExcept) {
			//JSON Issue...
			throw new FluidClientException(jsonExcept.getMessage(),
					FluidClientException.ErrorCode.JSON_PARSING);
		}
	}
}


/*
 * Koekiebox CONFIDENTIAL
 *
 * [2012] - [2017] Koekiebox (Pty) Ltd
 * All Rights Reserved.
 *
 * NOTICE: All information contained herein is, and remains the property
 * of Koekiebox and its suppliers, if any. The intellectual and
 * technical concepts contained herein are proprietary to Koekiebox
 * and its suppliers and may be covered by South African and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material is strictly
 * forbidden unless prior written permission is obtained from Koekiebox.
 */

package com.fluidbpm.program.api.util.sql.impl;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;

import com.fluidbpm.program.api.util.UtilGlobal;
import com.fluidbpm.program.api.util.cache.CacheUtil;
import com.fluidbpm.program.api.util.sql.ABaseSQLUtil;
import com.fluidbpm.program.api.util.sql.exception.FluidSQLException;
import com.fluidbpm.program.api.util.sql.syntax.ISyntax;
import com.fluidbpm.program.api.util.sql.syntax.SyntaxFactory;
import com.fluidbpm.program.api.vo.ABaseFluidVO;
import com.fluidbpm.program.api.vo.field.Field;
import com.fluidbpm.program.api.vo.field.MultiChoice;
import com.fluidbpm.program.api.vo.field.TableField;
import com.fluidbpm.program.api.vo.form.Form;

/**
 * SQL Utility class used for {@code Field} related actions.
 *
 * @author jasonbruwer
 * @since v1.0
 *
 * @see ABaseSQLUtil
 * @see Field
 */
public class SQLFormFieldUtil extends ABaseSQLUtil{

	private Map<Long, List<FormFieldMapping>> localDefinitionToFieldsMapping;
	private SQLFormDefinitionUtil sqlFormDefinitionUtil;

	/**
	 * Fluid mapping for a Form Field.
	 *
	 * @see ABaseFluidVO
	 */
	public static class FormFieldMapping extends ABaseFluidVO{

		public Long formDefinitionId;
		public Long formFieldId;
		public Long dataType;
		public String metaData;
		public String name;
		public String description;

		/**
		 * Form Field mapping.
		 *
		 * @param formDefinitionIdParam Form Definition Identifier.
		 * @param formFieldIdParam Form Field Identifier.
		 * @param dataTypeParam Form Field Data Type.
		 * @param metaDataParam Form Field Meta-Data.
		 * @param nameParam Form Field Name.
		 * @param descriptionParam Form Field Description.
		 *
		 * @see Field.Type
		 */
		public FormFieldMapping(
				Long formDefinitionIdParam,
				Long formFieldIdParam,
				Long dataTypeParam,
				String metaDataParam,
				String nameParam,
				String descriptionParam) {
			this.formDefinitionId = formDefinitionIdParam;
			this.formFieldId = formFieldIdParam;
			this.dataType = dataTypeParam;
			this.metaData = metaDataParam;
			this.name = nameParam;
			this.description = descriptionParam;
		}
	}

	/**
	 * New FormField util instance using {@code connectionParam}.
	 *
	 * @param connectionParam SQL Connection to use for Fields.
	 */
	public SQLFormFieldUtil(Connection connectionParam) {
		this(connectionParam, null);
	}

	/**
	 * New FormField util instance using {@code connectionParam}.
	 *
	 * @param connectionParam SQL Connection to use for Fields.
	 * @param cacheUtilParam The Cache Util for better performance.
	 */
	public SQLFormFieldUtil(Connection connectionParam, CacheUtil cacheUtilParam) {
		super(connectionParam, cacheUtilParam);

		this.localDefinitionToFieldsMapping = new HashMap();
	}

	/**
	 * New FormField util instance using {@code connectionParam}.
	 *
	 * @param connectionParam SQL Connection to use for Fields.
	 * @param cacheUtilParam The Cache Util for better performance.
	 * @param sqlFormDefinitionUtilParam The SQL Form Def utility for form definitions.
	 *                                   Useful for Table Fields.
	 */
	public SQLFormFieldUtil(
			Connection connectionParam,
			CacheUtil cacheUtilParam,
			SQLFormDefinitionUtil sqlFormDefinitionUtilParam) {
		this(connectionParam, cacheUtilParam);

		this.sqlFormDefinitionUtil = sqlFormDefinitionUtilParam;
	}

	/**
	 * Retrieve the Form Field mappings for Electronic Form {@code electronicFormIdParam}.
	 *
	 * @param electronicFormIdParam The Electronic Form to get Form Field mappings for.
	 * @return List of Form Field mappings.
	 */
	public List<FormFieldMapping> getFormFieldMappingForForm(
			Long electronicFormIdParam)
	{
		List<FormFieldMapping> returnVal = new ArrayList();

		if(electronicFormIdParam == null)
		{
			return returnVal;
		}

		PreparedStatement preparedStatement = null;
		ResultSet resultSet = null;
		try {
			Long formDefinitionId = this.getFormDefinitionId(electronicFormIdParam);

			//Local Mapping...
			//When we have the key by definition, we can just return.
			if(this.localDefinitionToFieldsMapping.containsKey(formDefinitionId))
			{
				return this.localDefinitionToFieldsMapping.get(formDefinitionId);
			}

			ISyntax syntax = SyntaxFactory.getInstance().getSyntaxFor(
					this.getSQLTypeFromConnection(),
					ISyntax.ProcedureMapping.Field.GetFormFieldsForFormContainer);

			preparedStatement = this.getConnection().prepareStatement(
					syntax.getPreparedStatement());

			preparedStatement.setLong(1, electronicFormIdParam);

			resultSet = preparedStatement.executeQuery();

			//Iterate each of the form containers...
			while (resultSet.next())
			{
				returnVal.add(this.mapFormFieldMapping(resultSet));
			}

			//Cache the mapping...
			this.localDefinitionToFieldsMapping.put(formDefinitionId, returnVal);

			return returnVal;
		} catch (SQLException sqlError) {
			throw new FluidSQLException(sqlError);
		} finally {
			this.closeStatement(preparedStatement, resultSet);
		}
	}

	/**
	 * Retrieve the Form Field mappings for Form Definition {@code electronicFormDefinitionIdParam}.
	 *
	 * @param formDefinitionIdParam The Electronic Form Definition
	 *                                        to get Form Field mappings for.
	 * @return List of Form Field mappings.
	 */
	public List<FormFieldMapping> getFormFieldMappingForFormDefinition(
			Long formDefinitionIdParam
	) {
		List<FormFieldMapping> returnVal = new ArrayList();

		if(formDefinitionIdParam == null || formDefinitionIdParam.longValue() < 1)
		{
			return returnVal;
		}

		PreparedStatement preparedStatement = null;
		ResultSet resultSet = null;
		try{
			//Local Mapping...
			//When we have the key by definition, we can just return.
			if(this.localDefinitionToFieldsMapping.containsKey(formDefinitionIdParam))
			{
				return this.localDefinitionToFieldsMapping.get(formDefinitionIdParam);
			}

			ISyntax syntax = SyntaxFactory.getInstance().getSyntaxFor(
					this.getSQLTypeFromConnection(),
					ISyntax.ProcedureMapping.Field.GetFormFieldsForFormDefinition);

			preparedStatement = this.getConnection().prepareStatement(
					syntax.getPreparedStatement());

			preparedStatement.setLong(1, formDefinitionIdParam);

			resultSet = preparedStatement.executeQuery();

			//Iterate each of the form containers...
			while (resultSet.next())
			{
				returnVal.add(this.mapFormFieldMapping(resultSet));
			}

			this.localDefinitionToFieldsMapping.put(formDefinitionIdParam, returnVal);

			return returnVal;
		}
		//
		catch (SQLException sqlError) {
			throw new FluidSQLException(sqlError);
		}
		//
		finally {
			this.closeStatement(preparedStatement, resultSet);
		}
	}

	/**
	 * Retrieves the Form Definition Id for the
	 * Electronic Form with id {@code electronicFormIdParam}.
	 *
	 * @param electronicFormIdParam The Electronic Form to fetch fields for.
	 * @return The Form Definition for Electronic Form {@code electronicFormIdParam}.
	 */
	public Long getFormDefinitionId(Long electronicFormIdParam)
	{
		if(electronicFormIdParam == null)
		{
			return null;
		}

		PreparedStatement preparedStatement = null;
		ResultSet resultSet;
		try {
			ISyntax syntax = SyntaxFactory.getInstance().getSyntaxFor(
					this.getSQLTypeFromConnection(),
					ISyntax.ProcedureMapping.Field.GetFormDefinitionForFormContainer);

			preparedStatement = this.getConnection().prepareStatement(
					syntax.getPreparedStatement());

			preparedStatement.setLong(1, electronicFormIdParam);

			resultSet = preparedStatement.executeQuery();

			//Iterate each of the form containers...
			while (resultSet.next())
			{
				return resultSet.getLong(1);
			}

			return null;
		} catch (SQLException sqlError) {
			throw new FluidSQLException(sqlError);
		} finally {
			this.closeStatement(preparedStatement);
		}
	}

	/**
	 * Retrieves the Form Fields {@code VALUES} for the
	 * Electronic Form with id {@code electronicFormIdParam}.
	 *
	 * @param electronicFormIdParam The Electronic Form to fetch fields for.
	 * @param includeTableFieldsParam Whether to populate the table fields.
	 * @param includeTableFieldFormRecordInfoParam Does table record form data need to be included.
	 *
	 * @return The Form Fields for Electronic Form {@code electronicFormIdParam}.
	 */
	public List<Field> getFormFields(
			Long electronicFormIdParam,
			boolean includeTableFieldsParam,
			boolean includeTableFieldFormRecordInfoParam
	) {
		List<Field> returnVal = new ArrayList();

		if(electronicFormIdParam == null) {
			return returnVal;
		}

		List<FormFieldMapping> fieldMappings =
				this.getFormFieldMappingForForm(electronicFormIdParam);

		if(fieldMappings == null || fieldMappings.isEmpty()) {
			return returnVal;
		}

		//Get the values for each of the fields...
		for(FormFieldMapping fieldMapping : fieldMappings) {
			//Skip if ignore Table Fields...
			if(!includeTableFieldsParam &&
					fieldMapping.dataType == UtilGlobal.FieldTypeId._7_TABLE_FIELD){//Table Field...
				continue;
			}

			Field fieldToAdd = this.getFormFieldValueFor(
					fieldMapping,
					electronicFormIdParam,
					includeTableFieldFormRecordInfoParam);

			if(fieldToAdd == null) {
				continue;
			}

			//When table field...
			if(includeTableFieldsParam && (fieldToAdd.getFieldValue() instanceof TableField)) {
				TableField tableField = (TableField)fieldToAdd.getFieldValue();

				if(tableField.getTableRecords() != null && !tableField.getTableRecords().isEmpty()) {
					for(Form tableRecordForm : tableField.getTableRecords()) {
						tableRecordForm.setFormFields(
								this.getFormFields(
										tableRecordForm.getId(),
										false,
										false));
					}
				}
			}

			returnVal.add(fieldToAdd);
		}

		return returnVal;
	}

	/**
	 * Retrieves the Form Field value for Electronic Form {@code formContainerIdParam}.
	 *
	 * @param formFieldMappingParam The mapping to use.
	 * @param formContainerIdParam The Electronic Form Id.
	 * @param includeTableFieldFormRecordInfoParam Does table record form data need to be included.
	 *
	 * @return Populated {@code Field} value.
	 */
	public Field getFormFieldValueFor(
			FormFieldMapping formFieldMappingParam,
			Long formContainerIdParam,
			boolean includeTableFieldFormRecordInfoParam
	) {
		if(formFieldMappingParam == null) {
			return null;
		}

		//First attempt to fetch from the cache...
		if(this.getCacheUtil() != null) {
			CacheUtil.CachedFieldValue cachedFieldValue =
					this.getCacheUtil().getCachedFieldValueFrom(
						formFieldMappingParam.formDefinitionId,
						formContainerIdParam,
						formFieldMappingParam.formFieldId);

			if(cachedFieldValue != null)
			{
				Field field = cachedFieldValue.getCachedFieldValueAsField();
				if(field != null)
				{
					field.setFieldName(formFieldMappingParam.name);
					return field;
				}
			}
		}

		//Now use a database lookup...
		Field returnVal = null;
		PreparedStatement preparedStatement = null, preparedStatementForTblInfo = null;
		ResultSet resultSet = null,resultSetForTblInfo = null;
		try {
			ISyntax syntax = SyntaxFactory.getInstance().getFieldValueSyntaxFor(
					this.getSQLTypeFromConnection(),
					formFieldMappingParam);

			if(syntax != null) {
				preparedStatement = this.getConnection().prepareStatement(
						syntax.getPreparedStatement());

				preparedStatement.setLong(1, formFieldMappingParam.formDefinitionId);
				preparedStatement.setLong(2, formFieldMappingParam.formFieldId);
				preparedStatement.setLong(3, formContainerIdParam);

				resultSet = preparedStatement.executeQuery();
			}

			switch (formFieldMappingParam.dataType.intValue()) {
				//Text...
				case UtilGlobal.FieldTypeId._1_TEXT:
				if(resultSet.next()) {
					returnVal = new Field(
							formFieldMappingParam.name,
							resultSet.getString(1),
							Field.Type.Text);
				}
				break;
				//True False...
				case UtilGlobal.FieldTypeId._2_TRUE_FALSE:
				if(resultSet.next()) {
					returnVal = new Field(
							formFieldMappingParam.name,
							resultSet.getBoolean(1),
							Field.Type.TrueFalse);
				}
				break;
				//Paragraph Text...
				case UtilGlobal.FieldTypeId._3_PARAGRAPH_TEXT:
				if(resultSet.next()) {
					returnVal = new Field(
							formFieldMappingParam.name,
							resultSet.getString(1),
							Field.Type.ParagraphText);
				}
				break;
				//Multiple Choice...
				case UtilGlobal.FieldTypeId._4_MULTI_CHOICE:
					MultiChoice multiChoice = new MultiChoice();

					List<String> selectedValues = new ArrayList();
					while(resultSet.next()) {
						selectedValues.add(resultSet.getString(1));
					}

					multiChoice.setSelectedMultiChoices(selectedValues);
					if(!selectedValues.isEmpty()) {
						returnVal = new Field(
								formFieldMappingParam.name,
								multiChoice);
					}
				break;
				//Date Time...
				case UtilGlobal.FieldTypeId._5_DATE_TIME:
					if(resultSet.next()) {
						returnVal = new Field(
								formFieldMappingParam.name,
								resultSet.getDate(1),
								Field.Type.DateTime);
					}
				break;
				//Decimal...
				case UtilGlobal.FieldTypeId._6_DECIMAL:
					if(resultSet.next()) {
						returnVal = new Field(
								formFieldMappingParam.name,
								resultSet.getDouble(1),
								Field.Type.Decimal);
					}
				break;
				//Table Field...
				case UtilGlobal.FieldTypeId._7_TABLE_FIELD:
					List<Long> formContainerIds = new ArrayList();
					while(resultSet.next()) {
						formContainerIds.add(resultSet.getLong(1));
					}

					//Break if empty...
					if(formContainerIds.isEmpty()) {
						break;
					}

					TableField tableField = new TableField();

					final List<Form> formRecords = new ArrayList();

					//Populate all the ids for forms...
					formContainerIds.forEach(formContId -> {
						formRecords.add(new Form(formContId));
					});

					//Retrieve the info for the table record...
					if(includeTableFieldFormRecordInfoParam) {

						ISyntax syntaxForFormContInfo = SyntaxFactory.getInstance().getSyntaxFor(
								this.getSQLTypeFromConnection(),
								ISyntax.ProcedureMapping.Form.GetFormContainerInfo);

						preparedStatementForTblInfo = this.getConnection().prepareStatement(
								syntaxForFormContInfo.getPreparedStatement());

						for(Form formRecordToSetInfoOn : formRecords) {

							preparedStatementForTblInfo.setLong(
									1, formRecordToSetInfoOn.getId());

							resultSetForTblInfo = preparedStatementForTblInfo.executeQuery();
							if(resultSetForTblInfo.next()) {

								Long formTypeId = resultSetForTblInfo.getLong(
										SQLFormUtil.SQLColumnIndex._02_FORM_TYPE);

								formRecordToSetInfoOn.setFormTypeId(formTypeId);
								formRecordToSetInfoOn.setFormType(
										this.sqlFormDefinitionUtil == null ? null :
												this.sqlFormDefinitionUtil.getFormDefinitionIdAndTitle().get(formTypeId)
								);

								formRecordToSetInfoOn.setTitle(resultSetForTblInfo.getString(
										SQLFormUtil.SQLColumnIndex._03_TITLE));

								Date created = resultSetForTblInfo.getDate(SQLFormUtil.SQLColumnIndex._04_CREATED);
								Date lastUpdated = resultSetForTblInfo.getDate(SQLFormUtil.SQLColumnIndex._05_LAST_UPDATED);

								//Created...
								if(created != null)
								{
									formRecordToSetInfoOn.setDateCreated(new Date(created.getTime()));
								}

								//Last Updated...
								if(lastUpdated != null)
								{
									formRecordToSetInfoOn.setDateLastUpdated(new Date(lastUpdated.getTime()));
								}
							}
						}
					}

					tableField.setTableRecords(formRecords);

					returnVal = new Field(
							formFieldMappingParam.name,
							tableField,
							Field.Type.Table);
					//TODO __8__ encrypted field...
				break;
				//Label...
				case UtilGlobal.FieldTypeId._9_LABEL:
					returnVal = new Field(
							formFieldMappingParam.name,
							formFieldMappingParam.description,
							Field.Type.Label);
					break;
				default:
					throw new SQLException("Unable to map '"+
							formContainerIdParam.intValue() +"', to Form Field value.");
			}

			return returnVal;
		} catch (SQLException sqlError) {
			throw new FluidSQLException(sqlError);
		} finally {
			this.closeStatement(preparedStatement);
			this.closeStatement(preparedStatementForTblInfo);
		}
	}

	/**
	 * Maps a {@code ResultSet} to a new instance of {@code FormFieldMapping}.
	 *
	 * @param resultSetParam The {@code ResultSet} used to create an {@code FormFieldMapping}.
	 * @return FormFieldMapping from {@code ResultSet}.
	 * @throws SQLException
	 *
	 * @see ResultSet
	 * @see FormFieldMapping
	 */
	private FormFieldMapping mapFormFieldMapping(ResultSet resultSetParam)
	throws SQLException {
		return new FormFieldMapping(
				resultSetParam.getLong(1),
				resultSetParam.getLong(2),
				resultSetParam.getLong(3),
				resultSetParam.getString(4),
				resultSetParam.getString(5),
				resultSetParam.getString(6));
	}

}


/*
 * Koekiebox CONFIDENTIAL
 *
 * [2012] - [2017] Koekiebox (Pty) Ltd
 * All Rights Reserved.
 *
 * NOTICE: All information contained herein is, and remains the property
 * of Koekiebox and its suppliers, if any. The intellectual and
 * technical concepts contained herein are proprietary to Koekiebox
 * and its suppliers and may be covered by South African and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material is strictly
 * forbidden unless prior written permission is obtained from Koekiebox.
 */

package com.fluidbpm.ws.client.v1.role;

import org.json.JSONException;
import org.json.JSONObject;

import com.fluidbpm.program.api.vo.role.Role;
import com.fluidbpm.program.api.vo.role.RoleListing;
import com.fluidbpm.program.api.vo.ws.WS;
import com.fluidbpm.ws.client.FluidClientException;
import com.fluidbpm.ws.client.v1.ABaseClientWS;

/**
 * Java Web Service Client for {@code Role} related actions.
 *
 * @author jasonbruwer
 * @since v1.0
 *
 * @see JSONObject
 * @see com.fluidbpm.program.api.vo.ws.WS.Path.Role
 * @see com.fluidbpm.program.api.vo.role.Role
 */
public class RoleClient extends ABaseClientWS {

    /**
     * Constructor that sets the Service Ticket from authentication.
     *
     * @param endpointBaseUrlParam URL to base endpoint.
     * @param serviceTicketParam The Server issued Service Ticket.
     */
    public RoleClient(
            String endpointBaseUrlParam,
            String serviceTicketParam) {
        super(endpointBaseUrlParam);

        this.setServiceTicket(serviceTicketParam);
    }

    /**
     * Creates a new {@code Role} with the privileges inside the {@code roleParam}.
     *
     * @param roleParam The {@code Role} to create.
     * @return The Created Role.
     *
     * @see Role
     * @see com.fluidbpm.program.api.vo.role.RoleToFormDefinition
     * @see com.fluidbpm.program.api.vo.role.RoleToFormFieldToFormDefinition
     * @see com.fluidbpm.program.api.vo.role.RoleToJobView
     */
    public Role createRole(Role roleParam)
    {
        if(roleParam != null && this.serviceTicket != null)
        {
            roleParam.setServiceTicket(this.serviceTicket);
        }

        return new Role(this.putJson(
                roleParam, WS.Path.Role.Version1.roleCreate()));
    }

    /**
     * Updates an existing {@code Role} with the privileges inside the {@code roleParam}.
     *
     * @param roleParam The {@code Role} to update.
     * @return The Created Role.
     *
     * @see Role
     * @see com.fluidbpm.program.api.vo.role.RoleToFormDefinition
     * @see com.fluidbpm.program.api.vo.role.RoleToFormFieldToFormDefinition
     * @see com.fluidbpm.program.api.vo.role.RoleToJobView
     */
    public Role updateRole(Role roleParam)
    {
        if(roleParam != null && this.serviceTicket != null)
        {
            roleParam.setServiceTicket(this.serviceTicket);
        }

        return new Role(this.postJson(
                roleParam, WS.Path.Role.Version1.roleUpdate()));
    }

    /**
     * Deletes the {@code Role} provided.
     * Id must be set on the {@code Role}.
     *
     * @param roleToDeleteParam The Role to Delete.
     * @return The deleted Role.
     */
    public Role deleteRole(
            Role roleToDeleteParam)
    {
        if(roleToDeleteParam != null && this.serviceTicket != null)
        {
            roleToDeleteParam.setServiceTicket(this.serviceTicket);
        }

        return new Role(this.postJson(roleToDeleteParam,
                WS.Path.Role.Version1.roleDelete()));
    }

    /**
     * Deletes the {@code Role} provided.
     * Id must be set on the {@code Role}.
     *
     * @param roleToDeleteParam The Role to Delete.
     * @param forcefullyDeleteParam Delete the Role forcefully.
     * @return The deleted Role.
     */
    public Role deleteRole(
            Role roleToDeleteParam,
            boolean forcefullyDeleteParam)
    {
        if(roleToDeleteParam != null && this.serviceTicket != null)
        {
            roleToDeleteParam.setServiceTicket(this.serviceTicket);
        }

        return new Role(this.postJson(roleToDeleteParam,
                WS.Path.Role.Version1.roleDelete(forcefullyDeleteParam)));
    }

    /**
     * Retrieves role information for the provided {@code roleIdParam}.
     *
     * @param roleIdParam The ID of the {@code Role} to retrieve info for.
     * @return Role information.
     *
     * @see com.fluidbpm.program.api.vo.role.Role
     */
    public Role getRoleById(Long roleIdParam)
    {
        Role roleToGetInfoFor = new Role();
        roleToGetInfoFor.setId(roleIdParam);

        if(this.serviceTicket != null)
        {
            roleToGetInfoFor.setServiceTicket(this.serviceTicket);
        }

        try {
            return new Role(this.postJson(
                    roleToGetInfoFor, WS.Path.Role.Version1.getById()));
        }
        //
        catch (JSONException jsonExcept) {
            throw new FluidClientException(jsonExcept.getMessage(),
                    FluidClientException.ErrorCode.JSON_PARSING);
        }
    }

    /**
     * Retrieves all role information.
     *
     * @return Role information.
     *
     * @see com.fluidbpm.program.api.vo.role.RoleListing
     */
    public RoleListing getAllRoles()
    {
        RoleListing roleToGetInfoFor = new RoleListing();

        if(this.serviceTicket != null)
        {
            roleToGetInfoFor.setServiceTicket(this.serviceTicket);
        }

        try {
            return new RoleListing(this.postJson(
                    roleToGetInfoFor, WS.Path.Role.Version1.getAllRoles()));
        }
        //
        catch (JSONException jsonExcept) {
            throw new FluidClientException(jsonExcept.getMessage(),
                    FluidClientException.ErrorCode.JSON_PARSING);
        }
    }
}


/*
 * Koekiebox CONFIDENTIAL
 *
 * [2012] - [2017] Koekiebox (Pty) Ltd
 * All Rights Reserved.
 *
 * NOTICE: All information contained herein is, and remains the property
 * of Koekiebox and its suppliers, if any. The intellectual and
 * technical concepts contained herein are proprietary to Koekiebox
 * and its suppliers and may be covered by South African and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material is strictly
 * forbidden unless prior written permission is obtained from Koekiebox.
 */

package com.fluidbpm.program.api.vo.mail;

import org.json.JSONException;
import org.json.JSONObject;

import com.fluidbpm.program.api.vo.ABaseFluidJSONObject;
import com.fluidbpm.program.api.vo.ABaseFluidVO;
import com.fluidbpm.program.api.vo.attachment.Attachment;

/**
 * Fluid representation of a Email message attachment.
 *
 * @author jasonbruwer
 * @since v1.0
 *
 * @see Attachment
 * @see MailMessage
 * @see MailMessageNameValue
 * @see Attachment
 * @see ABaseFluidVO
 */
public class MailMessageAttachment extends ABaseFluidJSONObject {

    public static final long serialVersionUID = 1L;

    private String attachmentPath;
    private String attachmentDataBase64;

    /**
     * The JSON mapping for the {@code MailMessageAttachment} object.
     */
    public static class JSONMapping
    {
        public static final String ATTACHMENT_PATH = "attachmentPath";
        public static final String ATTACHMENT_DATA_BASE64 = "attachmentDataBase64";
    }

    /**
     * Default constructor.
     */
    public MailMessageAttachment() {
        super();
    }

    /**
     * Sets the path to the attachment.
     *
     * @param attachmentPathParam Path to the attachment.
     */
    public MailMessageAttachment(String attachmentPathParam) {
        super();

        this.setAttachmentPath(attachmentPathParam);
    }

    /**
     * Populates local variables with {@code jsonObjectParam}.
     *
     * @param jsonObjectParam The JSON Object.
     */
    public MailMessageAttachment(JSONObject jsonObjectParam) {
        super(jsonObjectParam);

        if (this.jsonObject == null) {
            return;
        }

        //Attachment Path...
        if (!this.jsonObject.isNull(
                JSONMapping.ATTACHMENT_PATH)) {

            this.setAttachmentPath(this.jsonObject.getString(
                    JSONMapping.ATTACHMENT_PATH));
        }

        //Attachment Data-64...
        if (!this.jsonObject.isNull(
                JSONMapping.ATTACHMENT_DATA_BASE64)) {

            this.setAttachmentDataBase64(this.jsonObject.getString(
                    JSONMapping.ATTACHMENT_DATA_BASE64));
        }
    }

    /**
     * Conversion to {@code JSONObject} from Java Object.
     *
     * @return {@code JSONObject} representation of {@code MailMessageAttachment}
     * @throws JSONException If there is a problem with the JSON Body.
     *
     * @see ABaseFluidJSONObject#toJsonObject()
     */
    @Override
    public JSONObject toJsonObject() throws JSONException
    {
        JSONObject returnVal = super.toJsonObject();

        //Attachment Path...
        if(this.getAttachmentPath() != null)
        {
            returnVal.put(JSONMapping.ATTACHMENT_PATH,
                    this.getAttachmentPath());
        }

        //Attachment Data Base64...
        if(this.getAttachmentDataBase64() != null)
        {
            returnVal.put(JSONMapping.ATTACHMENT_DATA_BASE64,
                    this.getAttachmentDataBase64());
        }

        return returnVal;
    }

    /**
     * Gets the path to the attachment.
     *
     * @return Path to the attachment.
     */
    public String getAttachmentPath() {
        return this.attachmentPath;
    }

    /**
     * Sets the path to the attachment.
     *
     * @param attachmentPathParam Path to the attachment.
     */
    public void setAttachmentPath(String attachmentPathParam) {
        this.attachmentPath = attachmentPathParam;
    }

    /**
     * Gets the attachment data in {@code Base-64} format.
     *
     * @return Base-64 attachment content.
     */
    public String getAttachmentDataBase64() {
        return this.attachmentDataBase64;
    }

    /**
     * Sets the attachment data in {@code Base-64} format.
     *
     * @param attachmentDataBase64Param Base-64 attachment content.
     */
    public void setAttachmentDataBase64(String attachmentDataBase64Param) {
        this.attachmentDataBase64 = attachmentDataBase64Param;
    }
}


/*
 * Koekiebox CONFIDENTIAL
 *
 * [2012] - [2017] Koekiebox (Pty) Ltd
 * All Rights Reserved.
 *
 * NOTICE: All information contained herein is, and remains the property
 * of Koekiebox and its suppliers, if any. The intellectual and
 * technical concepts contained herein are proprietary to Koekiebox
 * and its suppliers and may be covered by South African and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material is strictly
 * forbidden unless prior written permission is obtained from Koekiebox.
 */

package com.fluidbpm.program.api.util.command;

import static com.fluidbpm.program.api.util.UtilGlobal.ENCODING_UTF_8;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

import com.fluidbpm.program.api.vo.ABaseFluidJSONObject;
import com.fluidbpm.program.api.vo.auth0.AccessTokenRequest;
import com.fluidbpm.program.api.vo.auth0.NormalizedUserProfile;

/**
 * Utility class used for executing command line operations.
 *
 * See more at: https://auth0.com/
 *
 * @author jasonbruwer
 * @since v1.6
 *
 * @see NormalizedUserProfile
 * @see AccessTokenRequest
 * @see ABaseFluidJSONObject
 */
public class CommandUtil {

	public static final String FLUID_CLI = "fluid-cli";

	/**
	 * Result value object when a command line operation is finish.
	 */
	public static final class CommandResult {
		private int exitCode;
		private String[] resultLines;

		/**
		 * Sets the exit code and result lines.
		 *
		 * @param exitCodeParam The exit code.
		 * @param resultLinesParam The result lines.
		 */
		private CommandResult(int exitCodeParam, String[] resultLinesParam) {
			this.exitCode = exitCodeParam;
			this.resultLines = resultLinesParam;
		}

		/**
		 * Gets the exit code.
		 *
		 * @return Exit Code.
		 */
		public int getExitCode() {
			return this.exitCode;
		}

		/**
		 * Gets the result lines.
		 *
		 * @return Result lines.
		 */
		public String[] getResultLines() {
			return this.resultLines;
		}

		/**
		 * Returns the {@code String[]} result lines
		 * as a single {@code String}.
		 *
		 * @return Result as a {@code String}.
		 */
		@Override
		public String toString() {

			if (this.resultLines == null) {
				return null;
			}

			StringBuilder stringBuilder = new StringBuilder();
			for (String line : this.resultLines) {
				stringBuilder.append(line);
				stringBuilder.append("\n");
			}

			return stringBuilder.toString();
		}
	}

	/**
	 * Executes the {@code commandParams} and returns the result.
	 *
	 * @param commandParams The command and parameters to execute.
	 * @return The result of the execution.
	 *
	 * @throws IOException If execution of the command fails.
	 *
	 * @see CommandResult
	 */
	public CommandResult executeCommand(String... commandParams) throws IOException {

		if (commandParams == null || commandParams.length == 0) {
			throw new IOException("Unable to execute command. No commands provided.");
		}

		List<String> returnedLines = new ArrayList();

		Charset charset = Charset.forName(ENCODING_UTF_8);

		try {
			Process process = null;
			//One param...
			if (commandParams.length == 1) {
				process = Runtime.getRuntime().exec(commandParams[0]);
			}
			//More params...
			else {
				process = Runtime.getRuntime().exec(commandParams);
			}

			BufferedReader reader = new BufferedReader(
					new InputStreamReader(
							process.getInputStream(), charset));

			String readLine = null;
			while ((readLine = reader.readLine()) != null) {
				returnedLines.add(readLine);
			}

			BufferedReader errorReader = new BufferedReader(
					new InputStreamReader(process.getErrorStream(),charset));

			while ((readLine = errorReader.readLine()) != null) {
				returnedLines.add(readLine);
			}

			int exitValue = -1000;
			try {
				exitValue = process.waitFor();
			} catch (InterruptedException e) {
				String commandString = (commandParams == null || commandParams.length == 0) ?
						"<unknown>" : commandParams[0];

				throw new IOException("Unable to wait for command [" + commandString +
						"] to exit. " + e.getMessage(), e);
			}

			String[] rtnArr = {};
			return new CommandResult(exitValue, returnedLines.toArray(rtnArr));
		} catch (IOException ioExeption) {
			//IO Problem...
			String commandString = (commandParams == null || commandParams.length == 0) ?
					"<unknown>" : commandParams[0];

			throw new IOException("Unable to execute command/s [" + commandString + "]. " + ioExeption.getMessage(), ioExeption);
		}
	}

	/**
	 * Executes the {@code objectCommandParam} and returns the result.
	 *
	 * @param objectCommandParam The command to execute.
	 * @return The result of the execution.
	 *
	 * @see CommandUtil#executeCommand(String...)
	 *
	 * @throws Exception If anything goes wrong during execution.
	 */
	public CommandResult executeCommand(String objectCommandParam) throws Exception {
		if (objectCommandParam == null) {
			return new CommandResult(333, new String[] {
					"No Object Command provided. 'null' not allowed." });
		}

		return this.executeCommand(new String[] { objectCommandParam });
	}
}