/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.hadoop.hive.ql.udf;

import org.apache.hadoop.hive.ql.exec.Description;
import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.io.Text;

/**
 * UDFUnhex.
 *
 */
@Description(name = "unhex",
    value = "_FUNC_(str) - Converts hexadecimal argument to binary",
    extended = "Performs the inverse operation of HEX(str). That is, it interprets\n"
    + "each pair of hexadecimal digits in the argument as a number and\n"
    + "converts it to the byte representation of the number. The\n"
    + "resulting characters are returned as a binary string.\n\n"
    + "Example:\n"
    + "> SELECT DECODE(UNHEX('4D7953514C'), 'UTF-8') from src limit 1;\n"
    + "'MySQL'\n\n"
    + "The characters in the argument string must be legal hexadecimal\n"
    + "digits: '0' .. '9', 'A' .. 'F', 'a' .. 'f'. If UNHEX() encounters\n"
    + "any nonhexadecimal digits in the argument, it returns NULL. Also,\n"
    + "if there are an odd number of characters a leading 0 is appended.")
public class UDFUnhex extends UDF {

  /**
   * Convert every two hex digits in s into.
   *
   */
  public byte[] evaluate(Text s) {
    if (s == null) {
      return null;
    }

    // append a leading 0 if needed
    String str;
    if (s.getLength() % 2 == 1) {
      str = "0" + s.toString();
    } else {
      str = s.toString();
    }

    byte[] result = new byte[str.length() / 2];
    for (int i = 0; i < str.length(); i += 2) {
      try {
        result[i / 2] = ((byte) Integer.parseInt(str.substring(i, i + 2), 16));
      } catch (NumberFormatException e) {
        // invalid character present, return null
        return null;
      }
    }

    return result;
  }
}
