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     */
018    package org.apache.hadoop.fs;
019    
020    import java.io.IOException;
021    
022    import org.apache.commons.codec.DecoderException;
023    import org.apache.commons.codec.binary.Base64;
024    import org.apache.commons.codec.binary.Hex;
025    import org.apache.hadoop.classification.InterfaceAudience;
026    import org.apache.hadoop.classification.InterfaceStability;
027    
028    import 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
039    public 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    }