SiLeBAT/FSK-Lab

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

Summary

Maintainability
D
2 days
Test Coverage
package de.bund.bfr.knime.fsklab.v1_9.joiner;

import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.knime.core.node.ExecutionContext;

import de.bund.bfr.knime.fsklab.nodes.v1_9.NodeUtils;
import de.bund.bfr.knime.fsklab.nodes.v1_9.ScriptHandler;
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.metadata.swagger.Parameter;
import metadata.SwaggerUtil;

public class JoinerNodeUtil {



  /**
   * 
   * @param originalOutputParameters map that contains the globally unique parameter names
   * and links them to the local names of the parameters in the actual scripts (e.g. var12 = var)
   * @param suffix Can be JoinerNodeModel.SUFFIX_FIRST ("1") or SUFFIX_SECOND ("2") 
   * @return mapping of original parameter names for one of the two models in a combined FSK object
   */
  public static LinkedHashMap<String, String> getOriginalParameterNames(LinkedHashMap<String, String> originalOutputParameters, String suffix){

    LinkedHashMap<String, String> originalNamesMap = new LinkedHashMap<String, String>();

    // create a mapping for the output parameters so that the true (original) parameter name is preserved 
    // original parameter name maps to the current (local) parameter (from the script)
    for(Map.Entry<String, String> pair : originalOutputParameters.entrySet()) {
      if(pair.getValue().endsWith(suffix)) {
        originalNamesMap.put(pair.getKey(), pair.getValue().substring(0, pair.getValue().length() - 1));
      }
    }

    return originalNamesMap;
  }

  /**
   * 
   * Create a simulation only for one model (first or second) in a combined object
   * 
   * 
   * @param combinedSim Simulation containing parameters from the first AND the second model.
   * @param suffix Can be JoinerNodeModel.SUFFIX_FIRST ("1") or SUFFIX_SECOND ("2")
   * @return simulation containing only parameters from the first or second model in a combined FSK object
   */
  public static FskSimulation makeIndividualSimulation(FskSimulation combinedSim, String suffix) {
    FskSimulation fskSimulation = new FskSimulation(combinedSim.getName());

    combinedSim.getParameters().forEach((pId,pValue) -> {
      if(pId.endsWith(suffix) )
        fskSimulation.getParameters().put(pId.substring(0, pId.length() - 1), pValue); 
    });

    return fskSimulation;
  }

  /**
   * This method saves the output of a model script using its globally unique parameter name so 
   * they can't be overwritten by other models in the same session
   * 
   * @param originalOutputParameters map that contains the globally unique parameter names
   * and links them to the local names of the parameters in the actual scripts (e.g. var12 = var)
   * @param handler takes care of script execution
   * @param exec KNIME execution context
   */
  public static void saveOutputVariable(Map<String,String> originalOutputParameters,
      ScriptHandler handler,
      ExecutionContext exec) {


    // save output to the official name (with all the suffixes) so it doesn't get overwritten by subsequent model executions
    // saving is done on R evaluation level
    for(Map.Entry<String, String> pair : originalOutputParameters.entrySet() )
    {
      // key: output with all suffixes (globally unique name)
      // value: output name without all suffixes
      try {

        handler.runScript(pair.getKey() + "<-" + pair.getValue(), exec, false);

      } catch(Exception e) { }
    }
  }

  /**
   * Methods adds an identifier suffix to each parameter so it can be identified after the joining.
   * The suffix is currently a string in {"1","2"}, "1" meaning the parameter is from the first model,
   * "2" meaning it is from the second model 
   *  
   * 
   * @param firstModelParameters List of parameters from the first model to be joined. It gets the JoinerNodeModel.SUFFIX_FIRST suffix. 
   * @param secondModelParameters List of parameters from the second model to be joined. It gets the JoinerNodeModel.SUFFIX_SECOND suffix.
   */
  public static void addIdentifierToParameters( 
      List<Parameter> firstModelParameters, List<Parameter> secondModelParameters) {

    firstModelParameters.forEach(it -> it.setId(it.getId() + JoinerNodeModel.SUFFIX_FIRST));
    secondModelParameters.forEach(it -> it.setId(it.getId() + JoinerNodeModel.SUFFIX_SECOND));


  }

  /**
   * A helper method to check if the parameters have a SUFFIX_FIRST or SUFFIX_SECOND which means in
   * the case of false that the workflow needs upgrade.
   * 
   * @param params the parameters to be tested
   * @return
   */
  public static boolean parametersNeedUpdate(List<Parameter> params, String firstSuffix,
      String secondSuffix) {
    boolean needsUpdate = false;

    if (params != null && !params.isEmpty()) {
      String firstID = params.get(0).getId();
      char lastChar = firstID.charAt(firstID.length() - 1);
      if (!(firstID.endsWith(firstSuffix) || (firstID.endsWith(secondSuffix)))) {
        needsUpdate = true;
      } else {
        for (Parameter param : params.subList(1, params.size())) {
          String paramID = param.getId();
          char currectLastChar = paramID.charAt(paramID.length() - 1);
          if (currectLastChar == lastChar) {
            if (!(paramID.endsWith(firstSuffix) || (paramID.endsWith(secondSuffix)))) {
              needsUpdate = true;
            }
          } else {
            needsUpdate = false;
            break;
          }
        }
      }
    }
    return needsUpdate;
  }

  /**
   * This method sets the default values of a combined model. The values are taken from the
   * simulation settings of the individual models.
   * 
   * 
   * @param first Simulation parameters from the first model to be joined.
   * @param second Simulation parameters from the second model to be joined.
   * @param parameters Simulation parameters with changed values based on the first and second
   *        simulations.
   */
  public static void createDefaultParameterValues(FskSimulation first, FskSimulation second, List<Parameter> parameters ) {

    for (Parameter p : parameters) {

      String p_id = p.getId();  // parameter id with suffix "1" or "2"
      String p_id_trim = p_id.substring(0, p_id.length() - 1);

      if(p_id.endsWith("1") && first.getParameters().containsKey(p_id_trim)) {
        p.setValue(first.getParameters().get(p_id_trim));
      }

      if(p_id.endsWith("2") && second.getParameters().containsKey(p_id_trim)) {
        p.setValue(second.getParameters().get(p_id_trim));
      }
    }
  }
  
  /**
   * This method creates a cross product of all simulations from the first and second models and
   * combines them to create simulations for the joined model.  
   * 
   * @param firstFskObj First FSK object with simulations 
   * @param secondFskObj Second FSK object with simulations
   * @param outObj Combined FSK object with new list of simulations from both models
   */
  public static void createAllPossibleSimulations(
      FskPortObject firstFskObj,
      FskPortObject secondFskObj, 
      CombinedFskPortObject outObj ) {
    
    int indexFirst = 0;
    int indexSecond = 0;
    for(FskSimulation simFirst : firstFskObj.simulations) {
      
      for(FskSimulation simSecond : secondFskObj.simulations) {
        
        // these simulations are already used for the default simulation of the combined model. So skip it.
        if(indexFirst == firstFskObj.selectedSimulationIndex && indexSecond == secondFskObj.selectedSimulationIndex) {
          indexSecond++;
          continue;
        }
        
        
        List<Parameter> combinedModelParameters = SwaggerUtil.getParameter(outObj.modelMetadata);

        FskSimulation simComb = new FskSimulation(simFirst.getName() + "_" + simSecond.getName());
        
        // unless the parameter is output, add it to new simulation
        for(Parameter p : combinedModelParameters) {
            if(p.getClassification().equals(Parameter.ClassificationEnum.OUTPUT))
              continue;
            
            
            String p_id = p.getId();
            // for convenience remove last suffix from the parameter to make comparison easier
            String p_trim = p.getId().substring(0, p.getId().length() - 1);
            // if simulation-parameter belongs to first model, get its value and add it to the combined simulation
            if(p_id.endsWith(JoinerNodeModel.SUFFIX_FIRST) ) {
              simComb.getParameters().put(p_id, simFirst.getParameters().get(p_trim));
            }
            if(p_id.endsWith(JoinerNodeModel.SUFFIX_SECOND) ) {
              simComb.getParameters().put(p_id, simSecond.getParameters().get(p_trim));
            }
          }
          
        // add new simulation to combined model
        outObj.simulations.add(simComb);
        indexSecond++;
      }
      indexSecond = 0;
      indexFirst++;
      
    }
    
  }
  
  
  
  
  /**
   * Create a default simulation for an FSKPortObject
   * @param fskObj The FSK Port Object 
   */
  public static void createDefaultSimulation(FskPortObject fskObj) {

    if (SwaggerUtil.getModelMath(fskObj.modelMetadata) != null) {

      List<Parameter> combinedModelParameters = SwaggerUtil.getParameter(fskObj.modelMetadata);

      FskSimulation defaultSimulation = NodeUtils.createDefaultSimulation(combinedModelParameters);

      fskObj.simulations.add(defaultSimulation);
      fskObj.selectedSimulationIndex = 0;
    }    
  }

  /**
   * This method removes INPUT parameters that are the target for a join command. 
   * 
   * @param relations The join relation between a source parameter (output of model 1) and target (input of model 2)
   * @param outfskPort The combined FSK PortObject with its List of parameters from the metadata
   */
  public static void removeJoinedParameters(JoinRelation[] relations, FskPortObject outfskPort) {

    if (relations != null)
      for (JoinRelation relation : relations) {

        Iterator<Parameter> iter = SwaggerUtil.getParameter(outfskPort.modelMetadata).iterator();
        while (iter.hasNext()) {
          Parameter p = iter.next();
                    
          // remove input from second model
          Boolean b2 = p.getId().equals(relation.getTargetParam());
          if (b2)
            iter.remove();
          
        } // while
      } // for
  }// resolveParameters
  
  /**
   * 
   * @param firstParameterList List of parameters from model 1
   * @param secondParameterList List of parameters from model 2
   * @return List of parameters from both models
   */
  public static List<Parameter> combineParameters(List<Parameter> firstParameterList,
      List<Parameter> secondParameterList) {

    // parameters
    List<Parameter> combinedList = Stream.of(firstParameterList, secondParameterList)
        .flatMap(x -> x.stream()).collect(Collectors.toList());

    return combinedList;
  }
  
  
  
}//JoinerNodeUtil