---
version: 20
data:

- type: "ModificationMethod"
  variation: null
  replacePermissions: true
  replaceObject: true
  attributes:
    category: "Identity"
    code: "|-
  /*
   * *****************************************************************************
   * The following variables are available in this script: 
   * ===================================================== 
   * ctx             : A context object that offers a number of methods. 
   *                   The method indexOf() can be used to get the index of a 
   *                   specific column within the dataArray. 
   * dataArray       : The data array. This array contains the contents of all 
   *                   columns of the current row of the table that is being 
   *                   masked by this method.
   *
   * Additionally for column based modification methods the following two
   * variables are available:
   * columnName      : The name of the column that matched the pattern.
   * columnIndex     : The position of the column that matched the pattern within
   *                   the data array. This value is zero-based.
   * *****************************************************************************
   */
  importClass(Packages.de.ubs.xdm3.batch.modification.HashUtils);
  /*
   * This function is called once before the processing starts. The method
   * is called once per table. 
   */
  function init() {
  }
  /* 
   * This function should modify the row that is provided by the dataArray. The 
   * method is called for each single row of the table. 
   */
  function apply() {
      var seed = new java.lang.String(dataArray[columnIndex]).trim();
      var newTaxNumber = [];
      while (newTaxNumber.length < 10) {
          var isValidDigit = false;
          var candidateDigit = 0;
          var tries = 0; //Save guard to avoid a neverending loop
          while (!isValidDigit && tries < 1000) {
              candidateDigit = HashUtils.hashInt(HashUtils.hashStringLikeInt(seed + newTaxNumber.length + tries++)) % 10;            
              var candidate = newTaxNumber.slice(0);
              candidate.push(candidateDigit);
              isValidDigit = isOccurrencesValid(candidate);
          }
          if (tries == 1000) {
              throw new RuntimeException(\"Unable to find valid digit for tax number\");
          }
          newTaxNumber.push(candidateDigit);
      }
      dataArray[columnIndex] = newTaxNumber.join('') + getChecksum(newTaxNumber);
  }
  /*
   * This function is executed once after the modification of a table is complete. The method
   * is called once per table. 
   */
  function close() {
  }
  /* 
   *Check if the digits are valid in order and number of occurrences
   */
  function isOccurrencesValid(taxNumberArray) {
      // Check if first digit is not 0
      if (taxNumberArray[0] == 0) {
          return false;
      }
      var countCharacters = [];
      for (var i = 0; i < 10; ++i) {
          countCharacters[i] = 0;
      }
      // Count characters
      for each (d in taxNumberArray) {
          countCharacters[d] += 1;
      }
      // Check if more than 1 character occurs 2 or 3 times
      var num2or3occurrences = 0;    
      for each(cnt in countCharacters) {
          if (cnt > 3) {
              return false;
          }
          if (cnt >= 2) {
              num2or3occurrences++;
          }
          if (num2or3occurrences > 1) {
              return false;
          }
      }
      // check that a character does not occur more than twice in one row
      for (var i = 0; i < taxNumberArray.length - 2; i++) {
          if (taxNumberArray[i] == taxNumberArray[i + 1] && taxNumberArray[i] == taxNumberArray[i + 2]) {
              return false;
          }
      }
      return true;
  }
  /*
   * Generate the checksum
   */
  function getChecksum(taxNumberArray) {
      var n = 11;
      var m = 10;
      var digit;
      var product = m;
      var sum;
      for (var i = 0; i < taxNumberArray.length; ++i) {
          digit = taxNumberArray[i];
          sum = (digit + product) % m;
          if (sum == 0) {
               sum = m;
          }
          product = (2 * sum) % n;
      }
      var checkDigit = n - product;
      return (checkDigit == 10) ? 0 : checkDigit;
  }
"

    description:
      "|-
      This modification method takes the current value of the column and generates a new valid german tax number based on a calculated hash value.

      The rules for the tax number are:
      - 11 characters where the last one is a check sum
      - only one character is allowed to occur two or three times
      - if a character occurs three times, it can occur only two times in a row
      - the first character can not be 0"
    displayName: "German tax identification number/TIN"
    inputType: "COLUMN"
    javaScriptLibraries: []
    parameters: []