# Thu, 20 Aug 2026 13:11:52 GMT
---
version: 23
data:


- type: ModificationMethod
  attributes:
    displayName: IMEI
    description: |
          Generates an IMEI based on an existing IMEI and the specified seed value.
    scriptLanguage: "GROOVY"
    legacyScript: false
    inputType: "COLUMN"
    mappingTableContainers: []
    mappingTableAccessMode: READ
    code: |
        /*
         * *****************************************************************************
         * IMEI
         * *****************************************************************************
         *
         * 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 data.
         * data       : 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.
         * 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 IMEI is zero-based.
         * *****************************************************************************
         */
        import de.ubs.xdm3.batch.modification.ModificationException
        
        def init() {
        }
        
        def apply() {
            def seedValue = modificationMethod.seed.toString()
        
            def imei = data[columnIndex]
            if (!(imei ==~ /\d{15}/)) {
                throw new ModificationException("Invalid imei '${imei}' found, only numeric digits with a length of 15 allowed.")
            }
        
            def tacPart = imei[0..7]     // first 8 digits (15 - 15 to 15 - 7)
            def snrPart = imei[8..13]    // next 6 digits
        
            def snr = get6DigitHash(seedValue + snrPart)
            def checkDigit = getCheckDigit(tacPart + snr)
            data[columnIndex] = tacPart + snr + checkDigit
            return true
        }
        
        def close() {
        }
        
        def get6DigitHash(def value) {
            if (!value) {
                value = "1234567890"
            }
        
            int hash = 0
            for (int i = 0; i < value.length() || hash < 0; i++) {
                int chr = value.charAt(i % value.length()) as int
                hash = ((hash << 5) - hash) + chr
                hash = hash & 0xFFFFFFFF  // ensure 32bit
            }
        
            def ret = (hash % 1_000_000).toString().padLeft(6, '0')
            return ret
        }
        
        def getCheckDigit(def digits) {
            int sum = 0
            def length = digits.length()
        
            for (int i = 0; i < length; i++) {
                int digit = digits.charAt(length - i - 1).toString().toInteger()
                if (i % 2 == 0) {
                    digit *= 2
                    sum += (digit / 10) as int
                    sum += digit % 10
                } else {
                    sum += digit
                }
            }
        
            return (sum % 10 == 0) ? 0 : (((sum / 10) as int + 1) * 10) - sum
        }
    parameters:
    - displayName: Seed
      description: |
          Specifies the value that is used to calculate the hash.
      variableName: seed
      dataType: STRING
