001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.fs;
019
020import java.io.IOException;
021
022import org.apache.commons.codec.DecoderException;
023import org.apache.commons.codec.binary.Base64;
024import org.apache.commons.codec.binary.Hex;
025import org.apache.hadoop.classification.InterfaceAudience;
026import org.apache.hadoop.classification.InterfaceStability;
027
028import com.google.common.base.Preconditions;
029
030/**
031 * The value of <code>XAttr</code> is byte[], this class is to 
032 * covert byte[] to some kind of string representation or convert back.
033 * String representation is convenient for display and input. For example
034 * display in screen as shell response and json response, input as http
035 * or shell parameter. 
036 */
037@InterfaceAudience.Public
038@InterfaceStability.Stable
039public enum XAttrCodec {
040  /**
041   * Value encoded as text 
042   * string is enclosed in double quotes (\").
043   */
044  TEXT,
045  
046  /**
047   * Value encoded as hexadecimal string 
048   * is prefixed with 0x.
049   */
050  HEX,
051  
052  /**
053   * Value encoded as base64 string 
054   * is prefixed with 0s.
055   */
056  BASE64;
057  
058  private static final String HEX_PREFIX = "0x";
059  private static final String BASE64_PREFIX = "0s";
060  private static final Base64 base64 = new Base64(0);
061  
062  /**
063   * Decode string representation of a value and check whether it's 
064   * encoded. If the given string begins with 0x or 0X, it expresses
065   * a hexadecimal number. If the given string begins with 0s or 0S,
066   * base64 encoding is expected. If the given string is enclosed in 
067   * double quotes, the inner string is treated as text. Otherwise 
068   * the given string is treated as text. 
069   * @param value string representation of the value.
070   * @return byte[] the value
071   * @throws IOException
072   */
073  public static byte[] decodeValue(String value) throws IOException {
074    byte[] result = null;
075    if (value != null) {
076      if (value.length() >= 2) {
077        String en = value.substring(0, 2);
078        if (value.startsWith("\"") && value.endsWith("\"")) {
079          value = value.substring(1, value.length()-1);
080          result = value.getBytes("utf-8");
081        } else if (en.equalsIgnoreCase(HEX_PREFIX)) {
082          value = value.substring(2, value.length());
083          try {
084            result = Hex.decodeHex(value.toCharArray());
085          } catch (DecoderException e) {
086            throw new IOException(e);
087          }
088        } else if (en.equalsIgnoreCase(BASE64_PREFIX)) {
089          value = value.substring(2, value.length());
090          result = base64.decode(value);
091        }
092      }
093      if (result == null) {
094        result = value.getBytes("utf-8");
095      }
096    }
097    return result;
098  }
099  
100  /**
101   * Encode byte[] value to string representation with encoding. 
102   * Values encoded as text strings are enclosed in double quotes (\"), 
103   * while strings encoded as hexadecimal and base64 are prefixed with 
104   * 0x and 0s, respectively.
105   * @param value byte[] value
106   * @param encoding
107   * @return String string representation of value
108   * @throws IOException
109   */
110  public static String encodeValue(byte[] value, XAttrCodec encoding) 
111      throws IOException {
112    Preconditions.checkNotNull(value, "Value can not be null.");
113    if (encoding == HEX) {
114      return HEX_PREFIX + Hex.encodeHexString(value);
115    } else if (encoding == BASE64) {
116      return BASE64_PREFIX + base64.encodeToString(value);
117    } else {
118      return "\"" + new String(value, "utf-8") + "\"";
119    }
120  }
121}