SiLeBAT/FSK-Lab

View on GitHub
de.bund.bfr.knime.fsklab.deprecatednodes/src-1_9_0/de/bund/bfr/knime/fsklab/v1_9/reader/ReaderNodeUtil.java

Summary

Maintainability
C
1 day
Test Coverage
package de.bund.bfr.knime.fsklab.v1_9.reader;


import de.bund.bfr.knime.fsklab.v1_9.CombinedFskPortObject;
import de.bund.bfr.knime.fsklab.v1_9.FskPortObject;
import de.bund.bfr.knime.fsklab.v1_9.FskSimulation;
import de.bund.bfr.knime.fsklab.v1_9.JoinRelation;
import de.bund.bfr.knime.fsklab.v1_9.joiner.JoinerNodeModel;
import de.bund.bfr.metadata.swagger.Parameter;
import de.bund.bfr.metadata.swagger.Parameter.ClassificationEnum;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.stream.Collectors;
import metadata.SwaggerUtil;

/**
 * Utility class that updates combined models from FSK-Lab v.1.7.2 to be compatible with 1.8+. The
 * difference between the versions is entirely in the parameter suffixes, meaning this class will
 * contain methods to update the suffixes of parameters in the metadata, the simulations and
 * join-relations. Before updating, any model will be checked if their parameters adhere to the
 * standards of 1.8+.
 * 
 * @author SchueleT
 */
public class ReaderNodeUtil {

  private ReaderNodeUtil() {
  }

  /**
   * Updates a combined model from 1.7.2 to be compatible with 1.8+. The suffixes from all
   * parameters are updated in metadata, simulations an join relations. If the model is from 1.7.2
   * (or combined at all) will be checked by looking at the current parameter suffixes. The update
   * is recursive for the entire model tree.
   * 
   * @param fskObj The combined model to be updated
   */
  public static void updateSuffixes(FskPortObject fskObj) {

    if (fskObj instanceof CombinedFskPortObject) {

      CombinedFskPortObject obj = (CombinedFskPortObject) fskObj;

      // if children are combined models, update them first:
      if (obj.getFirstFskPortObject() instanceof CombinedFskPortObject) {
        updateSuffixes(obj.getFirstFskPortObject());
      }
      if (obj.getSecondFskPortObject() instanceof CombinedFskPortObject) {
        updateSuffixes(obj.getSecondFskPortObject());
      }

      // check if model needs an update at all:
      if (modelNeedsUpdate(fskObj)) {

        // Get the parameter names of from the first child model to help assigning the combined
        // model parameters.
        List<String> firstModelParameters =
            SwaggerUtil.getParameter(obj.getFirstFskPortObject().modelMetadata).stream()
                .map(Parameter::getId).collect(Collectors.toList());

        // update metadata
        addSuffixesToOldModel(firstModelParameters, SwaggerUtil.getParameter(obj.modelMetadata));

        // update simulations
        addSuffixesToOldSimulations(firstModelParameters, obj.simulations);

        // update join relations
        obj.setJoinerRelation(updateJoinRelations(firstModelParameters, obj.getJoinerRelation()));
      }
    }
  }

  /**
   * This method adds the correct suffix to each parameter from the metadata.
   * 
   * @param firstModelParameters Reference parameter id's from the first sub-model to compare with
   *        parameters of the combined model.
   * @param originalParameters The parameters which are updated by adding a legal suffix to them.
   */
  static void addSuffixesToOldModel(List<String> firstModelParameters,
      List<Parameter> originalParameters) {

    for (Parameter p : originalParameters) {
      
      p.setId(addSuffix(firstModelParameters, p.getId()));
    }
  }

  /**
   * Method to update the parameter id's contained in the join relations of a combined model to make
   * it compatible to FSK-Lab version 1.8+. Source parameter, target parameter and the parameter
   * mentioned in the join command are updated to have the appropriate suffix.
   * 
   * @param firstModelParameters Reference parameter id's from the first sub-model to compare with
   *        parameters of the combined model.
   * @param relations The join relations of the combined model to be updated.
   * @return An array of JoinRelation objects containing updated parameter id's.
   */
  static JoinRelation[] updateJoinRelations(List<String> firstModelParameters,
      JoinRelation[] relations) {


    List<JoinRelation> newRelations = new ArrayList<>();

    for (JoinRelation relation : relations) {

      String command =
          updateJoinCommand(firstModelParameters, relation.getCommand(), relation.getSourceParam());
      String source = addSuffix(firstModelParameters, relation.getSourceParam());
      String target = addSuffix(firstModelParameters, relation.getTargetParam());
      String language = relation.getLanguage_written_in();

      newRelations.add(new JoinRelation(source, target, command, language));

    }

    return newRelations.toArray(new JoinRelation[0]);
  }

  /**
   * Method to update parameters in the simulations of a combined model, so that they comply to
   * version 1.8+.
   * 
   * @param firstModelParameters Reference parameter id's from the first sub-model to compare with
   *        parameters of the combined model.
   * @param simulations Simulations containing the parameters of the combined models.
   */
  static void addSuffixesToOldSimulations(List<String> firstModelParameters,
      List<FskSimulation> simulations) {


    for (FskSimulation oldSim : simulations) {

      Iterator<String> iterator = oldSim.getParameters().keySet().iterator();
      LinkedHashMap<String, String> newSim = new LinkedHashMap<>();
      // Iterate over the keyset of the each simulations linkedHashMap. From each parameter take
      // the name and value and create a new simulation with the updated parameter id.
      while (iterator.hasNext()) {

        String oldName = iterator.next();
        String newName = addSuffix(firstModelParameters, oldName);

        newSim.put(newName, oldSim.getParameters().get(oldName));
        iterator.remove(); // old parameter (i.e. key) is removed from simulation

      }

      oldSim.getParameters().putAll(newSim);
    }
  }

  /**
   * Support method for adding brackets and the correct suffix to the parameter within a join
   * command.
   * 
   * @param firstModelParameters Reference parameter id's from the first sub-model to compare with
   *        parameters of the combined model.
   * @param cmd The Join command to be applied to the targetParameter.
   * @param sourceParam The source parameter to be updated.
   * @return Join Command with updated source parameter and brackets: "[source]"
   */
  private static String updateJoinCommand(List<String> firstModelParameters, String cmd,
      String sourceParam) {

    return cmd.replace(sourceParam, "[" + addSuffix(firstModelParameters, sourceParam) + "]");
  }

  /**
   * Method to add an official suffix to a parameter id. If the parameter has the suffix "_dup", it
   * will be replaced by the official suffix for the first model (only parameters from the first
   * model get the "_dup" suffix.
   * 
   * @param firstModelParameters Parameter id's from the first child model of a combined model
   * @param param A parameter id from any source that needs the suffix
   * @return New parameter id with correct suffix
   */
  static String addSuffix(List<String> firstModelParameters, String param) {

    if (param.endsWith("_dup")) {
      return param.substring(0, param.length() - 4) + JoinerNodeModel.SUFFIX_FIRST;
    }

    if (firstModelParameters.contains(param)) {
      return param + JoinerNodeModel.SUFFIX_FIRST;
    }
    return param + JoinerNodeModel.SUFFIX_SECOND;
  }

  /**
   * Method checks if a model needs to update its parameters to be compliant to FSK-Lab version
   * 1.8+. It compares the number of parameter in the metadata suffixes with the maximum depth of
   * the model. If the depth is greater than the number of legal suffixes, it means the model needs
   * an update to be executable in FSK-Lab.
   * 
   * @param fskObj The model that is to be checked.
   * @return true if the model is outdated and needs an update.
   */
  static boolean modelNeedsUpdate(FskPortObject fskObj) {

    if (fskObj instanceof CombinedFskPortObject) {

      int depthFirst = getNumberOfSubmodels(fskObj);
      int numberSuffixes = getNumberOfSuffixes(SwaggerUtil.getParameter(fskObj.modelMetadata));

      if (depthFirst > numberSuffixes) {
        return true;
      }
    }
    return false;
  }

  /**
   * Support method to get the highest number of suffixes that any parameter has in a model.
   * 
   * @param parameter List of parameters from the metadata of the combined model.
   * @return number of legal suffixes that a parameter can have. We only consider output parameters
   *         since they can't be overwritten by join commands.
   */
  private static int getNumberOfSuffixes(List<Parameter> params) {

    int maxNumber = 0;
    for (Parameter param : params) {

      if (param.getClassification().equals(ClassificationEnum.OUTPUT)) {

        int number = getNumberOfSuffixes(param.getId());
        if (maxNumber < number) {
          maxNumber = number;
        }
      }
    }

    return maxNumber;
  }

  /**
   * Method to count the number of legal suffixes of a parameter id.
   * 
   * @param param Parameter id.
   * @return number of legal suffixes.
   */
  private static int getNumberOfSuffixes(String param) {

    int number = 0;

    while (param.endsWith(JoinerNodeModel.SUFFIX_FIRST)
        || param.endsWith(JoinerNodeModel.SUFFIX_SECOND)) {

      number++;
      param = param.substring(0, param.length() - JoinerNodeModel.SUFFIX_FIRST.length());
    }

    return number;
  }

  /**
   * Method to determine the depth of a combined model, i.e. how many submodels a model has.
   * 
   * @param fskObj The combined model.
   * @return the maximum depth of a combined model.
   */
  private static int getNumberOfSubmodels(FskPortObject fskObj) {

    if (!(fskObj instanceof CombinedFskPortObject)) {
      return 0;
    }

    CombinedFskPortObject combObj = (CombinedFskPortObject) fskObj;

    int depthFirst = 1 + getNumberOfSubmodels(combObj.getFirstFskPortObject());
    int depthSecond = 1 + getNumberOfSubmodels(combObj.getSecondFskPortObject());

    return (depthFirst > depthSecond) ? depthFirst : depthSecond;
  }
}