package com.nttdata.calender.states; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; import com.bmc.arsys.api.ARException; import com.bmc.arsys.api.Entry; import com.nttdata.calender.api.Query; import com.nttdata.calender.api.RemedyJavaAPI; import com.nttdata.calender.errorhandling.ErrorTypes.NotFoundError; /** * The State class is a singleton class responsible for managing and querying * state-related information. It handles the retrieval and storage of state * names in english and german and possible state transitions. * The states get fetched once at the start of the server. */ public class State { private HashMap state; private static final State INSTANCE = new State(); /** * Initializes the state with an empty HashMap. */ private State() { this.state = new HashMap<>(); } /** * Returns the Singleton instance of the State. * * @return the Singleton instance of State */ public static State getInstance() { return INSTANCE; } /** * Returns the state. * * @return the state */ public HashMap getState() { return state; } /** * Sets the state. * * @param state the state to set */ public void setState(HashMap state) { this.state = state; } /** * Query for all the information about the state. First, the possible state * transitions are queried by {@code queryPossibleStates()}. The query consists * of * the actual state, the possible state, and the English state name. Then, the * state names in English and German are queried by {@code queryStateNames()}. * These pieces of information are then merged and saved into the {@link #state} * HashMap. * * @param api Remedy API object * @throws ARException */ public void queryState(RemedyJavaAPI api) throws ARException, NotFoundError { // queryStateNames(api); queryPossibleStates(api); if (state.isEmpty()) { throw new NotFoundError("No States found in this context"); } } /** * Query the specified form for the possible states. A state is encoded with an * integer and has one or multiple states that it could transition to. The state * and possible states are encoded as integers and saved as {@link StateInfo}. * Additionally, the English state name is queried and saved into the * {@link StateInfo}. * * @param api Remedy API object * @throws ARException if an error occurs during the query */ public void queryPossibleStates(RemedyJavaAPI api) throws ARException { var configurationQuery = new Query.QueryBuilder("ITSM:Configuration") .addFieldId("engName", 700003002) .addFieldId("gerName", 700003003) .addFieldId("cancelFlag", 700003004) .addFieldId("actualState", 700003005) .addFieldId("restartFlag", 700003010) .addFieldId("implementerFlag", 700003012) .addFieldId("possibleState", 700003006) // Include possibleState here .build(); var configurationStatusQuery = new Query.QueryBuilder("ITSM:Configuration") .addFieldId("actualState", 700003005) .addFieldId("possibleState", 700003006) .build(); var stateFields = api.queryFieldsById("\'Menu\' = \"CHANGE_CALENDER_STATUS\"", configurationQuery.getFieldIds(), configurationQuery.getFormName(), null, 0, 0); var stateStatusFields = api.queryFieldsById("\'Menu\' = \"CHANGE_CALENDER_STATUS_TRANSITIONS\"", configurationStatusQuery.getFieldIds(), configurationStatusQuery.getFormName(), null, 0, 0); populateStateInfo(stateFields, stateStatusFields, configurationQuery, configurationStatusQuery); // Print state information // printStateInfo(); } private void populateStateInfo(List stateFields, List stateStatusFields, Query configurationQuery, Query configurationStatusQuery) throws ARException { for (var stateField : stateFields) { int actualState = stateField.get(configurationQuery.getFieldId("actualState")).getIntValue(); boolean cancelFlag = false; boolean restartFlag = false; boolean implementerFlag = false; try { // Extract additional fields cancelFlag = stateField.get(configurationQuery.getFieldId("cancelFlag")).toString() .equals("CANCEL_YES"); restartFlag = stateField.get(configurationQuery.getFieldId("restartFlag")).toString() .equals("RESTARTALLOWED_YES"); implementerFlag = stateField.get(configurationQuery.getFieldId("implementerFlag")).toString() .equals("SETIMPLEMENTER_YES"); } catch (Exception e) { // e.printStackTrace(); } String engName = stateField.get(configurationQuery.getFieldId("engName")).toString(); String gerName = stateField.get(configurationQuery.getFieldId("gerName")).toString(); // Initialize StateInfo with additional fields StateInfo stateInfo = new StateInfo(engName, gerName, cancelFlag, restartFlag, implementerFlag); for (var statusField : stateStatusFields) { int statusActualState = statusField.get(configurationStatusQuery.getFieldId("actualState")) .getIntValue(); if (actualState == statusActualState) { int possibleState = statusField.get(configurationStatusQuery.getFieldId("possibleState")) .getIntValue(); stateInfo.addPossibleState(possibleState); } } // Add StateInfo to the state HashMap getState().put(actualState, stateInfo); } } private void printStateInfo() { for (var state : get()) { // Join possible states into a single string String possibleStatesString = String.join(", ", state.possibleStates.stream().map(Object::toString).collect(Collectors.toList())); // Print the state information System.out.printf("\n\n[%s]\n[%s]\n[%s]\n[%s]\n[%b]\n[%b]\n[%b]\n\n", state.actualState, state.stateNameDE, state.stateNameEN, possibleStatesString, state.cancelFlag, state.restartFlag, state.implementerFlag); // Print the number of possible states System.out.println("Possible States: " + state.possibleStates.size()); } } /** * Takes the relevant data about possible states out of the {@code List} * and processes them to be saved into the State. * * @param stateFields List of Entry to be processed * @param configurationQuery Query object of stateFields */ // public void updatePossibleStates(List stateFields, Query // configurationQuery) { // stateFields.stream().forEach(entry -> { // var actualState = // entry.get(configurationQuery.getFieldId("actualState")).getIntValue(); // var possibleState = // entry.get(configurationQuery.getFieldId("possibleState")).getIntValue(); // this.getState().get(actualState).addPossibleState(possibleState); // }); // } /** * Query the German state name from the specified form. The German name in the * form is associated with the English one. For merging the names into the * {@link StateInfo}, we return a HashMap that is composed of the English state * name as the key and the German state name as the value. * * @param api Remedy API object * @throws ARException if an error occurs during the query */ // private void queryStateNames(RemedyJavaAPI api) throws ARException { // var nameQuery = new Query.QueryBuilder("SYS:Menu Items Locale LkUp") // .addFieldId("englishName", 1000004339) // .addFieldId("germanName", 1000004338) // .addFieldId("Locale", 1000004342) // .addFieldId("SelectionCode", 1000004336) // .build(); // var stateNames = api.queryFieldsById("\'Menu Type\' = \"Change Status // Values\"", // nameQuery.getFieldIds(), // nameQuery.getFormName(), null, 0, 0); // updateStateNames(stateNames, nameQuery); // } /** * Takes the relevant data about state names out of the {@code List} and * processes them to be saved into the State. * * @param stateFields List of Entry to be processed * @param nameQuery Query object of stateFields */ // public void updateStateNames(List stateFields, Query nameQuery) { // stateFields.stream() // .filter(entry -> // Optional.ofNullable(entry.get(nameQuery.getFieldId("Locale"))) // .map(Object::toString) // .orElse("") // .equals("de")) // .forEach( // entry -> { // var selectionCode = nameQuery.getFieldId("SelectionCode"); // var englishName = nameQuery.getFieldId("englishName"); // var germanName = nameQuery.getFieldId("germanName"); // this.getState().put(entry.get(selectionCode).getIntValue(), // new StateInfo(entry.get(englishName).toString(), // entry.get(germanName).toString())); // }); // } /** * Generate an Array of JSON objects that consists of all the state information. * These are the integer representation of the actual state, an Array of integer * representations of possible states, the English name of the actual state, and * the German name of the actual state. * * @return Array of JSON objects with integer representation of state and * {@link StateInfo} */ public ArrayList get() { var response = new ArrayList(); this.state.forEach((key, value) -> { response.add(new StateResponse(key, value.possibleState, value.stateNameEN, value.stateNameDE, value.cancelFlag, value.restartFlag, value.implementerFlag)); }); return response; } /** * Represents the state info of a state. */ public class StateInfo { String stateNameEN; String stateNameDE; ArrayList possibleState; boolean cancelFlag; boolean restartFlag; boolean implementerFlag; /** * Initializes an instance of the {@link StateInfo} class. * * @param possibleState possible state encoded as an integer * @param stateNameEN state name in English * @param stateNameDE state name in German */ public StateInfo(int possibleState, String stateNameEN, String stateNameDE) { this.possibleState = new ArrayList<>(); this.possibleState.add(possibleState); this.stateNameDE = stateNameDE; this.stateNameEN = stateNameEN; } /** * Initializes an instance of the {@link StateInfo} class. * * @param stateNameEN state name in English * @param stateNameDE state name in German */ public StateInfo(String stateNameEN, String stateNameDE, boolean cancelFlag, boolean restartFlag, boolean implementerFlag) { this.stateNameEN = stateNameEN; this.stateNameDE = stateNameDE; this.cancelFlag = cancelFlag; this.restartFlag = restartFlag; this.implementerFlag = implementerFlag; this.possibleState = new ArrayList<>(); } /** * Adds a possible state to transition to, encoded as an integer, to the * {@link StateInfo}. * * @param possibleState possible state */ public void addPossibleState(int possibleState) { if (!this.possibleState.contains(possibleState)) { this.possibleState.add(possibleState); } } /** * Get the state name in English. * * @return the state name in English */ public String getStateNameEN() { return stateNameEN; } /** * Get the state name in German. * * @return the state name in German */ public String getStateNameDE() { return stateNameDE; } /** * Set the state name in German. * * @param stateNameDE the state name in German */ public void setStateNameDE(String stateNameDE) { this.stateNameDE = stateNameDE; } /** * Set the state name in English. * * @param stateNameEN the state name in English */ public void setStateNameEN(String stateNameEN) { this.stateNameEN = stateNameEN; } } }