View Javadoc

1   /**
2    * Copyright 2010-2012 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.common.util;
17  
18  import java.io.UnsupportedEncodingException;
19  
20  import org.apache.commons.lang3.CharSet;
21  import org.apache.commons.lang3.StringUtils;
22  
23  /**
24   * A few (highly inefficient) methods that, when given an encoding, can convert Java <code>String's</code> into the hex for that encoding
25   * and also convert the hex back into the original string.
26   */
27  public class HexUtils {
28  
29  	private static final String ZERO = "0";
30  	private static final int BYTE_MASK = 0x000000ff;
31  	private static final String[] HEX_RANGES = new String[] { "0-9", "A-F", "a-f" };
32  	private static final String HEX_RANGES_STRING = toString(HEX_RANGES);
33  	private static final CharSet HEX_CHARSET = CharSet.getInstance(HEX_RANGES);
34  
35  	public static final CharSet getHexCharSet() {
36  		return HEX_CHARSET;
37  	}
38  
39  	public static final String[] getHexRanges() {
40  		return HEX_RANGES;
41  	}
42  
43  	protected static final String toString(String[] tokens) {
44  		StringBuilder sb = new StringBuilder();
45  		sb.append("[");
46  		for (int i = 0; i < HEX_RANGES.length; i++) {
47  			if (i != 0) {
48  				sb.append(",");
49  			}
50  			sb.append(HEX_RANGES[i]);
51  		}
52  		sb.append("]");
53  		return sb.toString();
54  	}
55  
56  	/**
57  	 * Convert <code>string</code> into a <code>byte[]</code> using the specified encoding, then convert each <code>byte</code> into its 2
58  	 * digit hexadecimal form.
59  	 */
60  	public static String toHexString(String string, String encoding) throws UnsupportedEncodingException {
61  		byte[] bytes = encoding == null ? string.getBytes() : string.getBytes(encoding);
62  		return toHexString(bytes);
63  	}
64  
65  	/**
66  	 * Convert each <code>byte</code> into its 2 digit hexadecimal form.
67  	 */
68  	public static String toHexString(byte[] bytes) {
69  		StringBuilder sb = new StringBuilder();
70  		for (byte b : bytes) {
71  			int masked = BYTE_MASK & b;
72  			String hex = Integer.toHexString(masked).toUpperCase();
73  			String padded = StringUtils.leftPad(hex, 2, ZERO);
74  			sb.append(padded);
75  		}
76  		return sb.toString();
77  	}
78  
79  	/**
80  	 * Return true if every character is valid hex <code>0-9</code>, <code>a-f</code>, or <code>A-F</code>
81  	 */
82  	public static final boolean isHex(char... chars) {
83  		for (char c : chars) {
84  			if (!HEX_CHARSET.contains(c)) {
85  				return false;
86  			}
87  		}
88  		return true;
89  	}
90  
91  	/**
92  	 * Given a string in <code>strictly hex</code> format, return the corresponding <code>byte[]</code>. <code>strictly hex</code> in the
93  	 * context of this method means that the string:<br>
94  	 * 1 - Contains only the characters <code>a-f</code>, <code>A-F</code>, and <code>0-9</code><br>
95  	 * 2 - Its length is an even number.
96  	 */
97  	public static final byte[] getBytesFromHexString(String hex) {
98  		char[] chars = hex.toCharArray();
99  		int length = chars.length;
100 		if (length % 2 != 0) {
101 			throw new IllegalArgumentException("Invalid hex string [" + hex + "].  String must contain an even number of characters.  '" + length + "' is not an even number!");
102 		}
103 		byte[] bytes = new byte[length / 2];
104 		int byteIndex = 0;
105 		for (int i = 0; i < length; i += 2) {
106 			char c1 = chars[i];
107 			char c2 = chars[i + 1];
108 			if (!isHex(c1, c2)) {
109 				int byteNumber = i / 2 + 1;
110 				int c1Pos = i + 1;
111 				int c2Pos = i + 2;
112 				throw new IllegalArgumentException("Invalid hex string [" + hex + "].  Invalid hex detected at byte " + byteNumber + ", characters " + c1Pos + "-" + c2Pos
113 				        + ".  Both characters must be in the range " + HEX_RANGES_STRING);
114 			}
115 			String s = c1 + "" + c2;
116 			int integer = Integer.parseInt(s, 16);
117 			int masked = integer & BYTE_MASK;
118 			byte b = (byte) masked;
119 			bytes[byteIndex++] = b;
120 		}
121 		return bytes;
122 	}
123 
124 	/**
125 	 * Given a string in <code>strictly hex</code> format and the <code>encoding</code> that was used to produce the hex, convert it back to
126 	 * a Java <code>String</code>. <code>strictly hex</code> in the context of this method means that the string:<br>
127 	 * 1 - Contains only the characters <code>a-f</code>, <code>A-F</code>, and <code>0-9</code><br>
128 	 * 2 - Its length is an even number.
129 	 */
130 	public static final String toStringFromHex(String hex, String encoding) throws UnsupportedEncodingException {
131 		byte[] bytes = getBytesFromHexString(hex);
132 		return StringUtils.toString(bytes, encoding);
133 	}
134 
135 }