1 2 /* 3 * ==================================================================== 4 * The Apache Software License, Version 1.1 5 * 6 * Copyright (c) 2002 The Apache Software Foundation. All rights 7 * reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in 18 * the documentation and/or other materials provided with the 19 * distribution. 20 * 21 * 3. The end-user documentation included with the redistribution, 22 * if any, must include the following acknowledgment: 23 * "This product includes software developed by the 24 * Apache Software Foundation (http://www.apache.org/)." 25 * Alternately, this acknowledgment may appear in the software itself, 26 * if and wherever such third-party acknowledgments normally appear. 27 * 28 * 4. The names "Apache" and "Apache Software Foundation" and 29 * "Apache POI" must not be used to endorse or promote products 30 * derived from this software without prior written permission. For 31 * written permission, please contact apache@apache.org. 32 * 33 * 5. Products derived from this software may not be called "Apache", 34 * "Apache POI", nor may "Apache" appear in their name, without 35 * prior written permission of the Apache Software Foundation. 36 * 37 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 38 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 39 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 40 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR 41 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 42 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 43 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 44 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 45 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 46 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 47 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 48 * SUCH DAMAGE. 49 * ==================================================================== 50 * 51 * This software consists of voluntary contributions made by many 52 * individuals on behalf of the Apache Software Foundation. For more 53 * information on the Apache Software Foundation, please see 54 * <http://www.apache.org/>. 55 */ 56 package org.apache.poi.util; 57 58 import java.io.UnsupportedEncodingException; 59 60 import java.text.NumberFormat; 61 import java.text.FieldPosition; 62 63 /** 64 * Title: String Utility Description: Collection of string handling utilities 65 * 66 *@author Andrew C. Oliver 67 *@created May 10, 2002 68 *@version 1.0 69 */ 70 71 public class StringUtil { 72 /** 73 * Constructor for the StringUtil object 74 */ 75 private StringUtil() { } 76 77 78 /** 79 * given a byte array of 16-bit unicode characters, compress to 8-bit and 80 * return a string 81 * 82 *@param string the byte array to be converted 83 *@param offset the initial offset into the 84 * byte array. it is assumed that string[ offset ] and string[ offset + 85 * 1 ] contain the first 16-bit unicode character 86 *@param len 87 *@return the converted string 88 *@exception ArrayIndexOutOfBoundsException if offset is out of bounds for 89 * the byte array (i.e., is negative or is greater than or equal to 90 * string.length) 91 *@exception IllegalArgumentException if len is too large (i.e., 92 * there is not enough data in string to create a String of that 93 * length) 94 *@len the length of the final string 95 */ 96 97 public static String getFromUnicodeHigh(final byte[] string, 98 final int offset, final int len) 99 throws ArrayIndexOutOfBoundsException, IllegalArgumentException { 100 if ((offset < 0) || (offset >= string.length)) { 101 throw new ArrayIndexOutOfBoundsException("Illegal offset"); 102 } 103 if ((len < 0) || (((string.length - offset) / 2) < len)) { 104 throw new IllegalArgumentException("Illegal length"); 105 } 106 byte[] bstring = new byte[len]; 107 int index = offset; 108 // start with high bits. 109 110 for (int k = 0; k < len; k++) { 111 bstring[k] = string[index]; 112 index += 2; 113 } 114 return new String(bstring); 115 } 116 117 118 119 /** 120 * given a byte array of 16-bit unicode characters, compress to 8-bit and 121 * return a string 122 * 123 *@param string the byte array to be converted 124 *@param offset the initial offset into the 125 * byte array. it is assumed that string[ offset ] and string[ offset + 126 * 1 ] contain the first 16-bit unicode character 127 *@param len 128 *@return the converted string 129 *@exception ArrayIndexOutOfBoundsException if offset is out of bounds for 130 * the byte array (i.e., is negative or is greater than or equal to 131 * string.length) 132 *@exception IllegalArgumentException if len is too large (i.e., 133 * there is not enough data in string to create a String of that 134 * length) 135 *@len the length of the final string 136 */ 137 138 public static String getFromUnicode(final byte[] string, 139 final int offset, final int len) 140 throws ArrayIndexOutOfBoundsException, IllegalArgumentException { 141 if ((offset < 0) || (offset >= string.length)) { 142 throw new ArrayIndexOutOfBoundsException("Illegal offset"); 143 } 144 if ((len < 0) || (((string.length - offset) / 2) < len)) { 145 throw new IllegalArgumentException("Illegal length"); 146 } 147 byte[] bstring = new byte[len]; 148 int index = offset + 1; 149 // start with low bits. 150 151 for (int k = 0; k < len; k++) { 152 bstring[k] = string[index]; 153 index += 2; 154 } 155 return new String(bstring); 156 } 157 158 159 /** 160 * given a byte array of 16-bit unicode characters, compress to 8-bit and 161 * return a string 162 * 163 *@param string the byte array to be converted 164 *@return the converted string 165 */ 166 167 public static String getFromUnicode(final byte[] string) { 168 return getFromUnicode(string, 0, string.length / 2); 169 } 170 171 172 /** 173 * write compressed unicode 174 * 175 *@param input the String containing the data to be written 176 *@param output the byte array to which the data is to be written 177 *@param offset an offset into the byte arrat at which the data is start 178 * when written 179 */ 180 181 public static void putCompressedUnicode(final String input, 182 final byte[] output, 183 final int offset) { 184 int strlen = input.length(); 185 186 for (int k = 0; k < strlen; k++) { 187 output[offset + k] = (byte) input.charAt(k); 188 } 189 } 190 191 192 /** 193 * Write uncompressed unicode 194 * 195 *@param input the String containing the unicode data to be written 196 *@param output the byte array to hold the uncompressed unicode 197 *@param offset the offset to start writing into the byte array 198 */ 199 200 public static void putUncompressedUnicode(final String input, 201 final byte[] output, 202 final int offset) { 203 int strlen = input.length(); 204 205 for (int k = 0; k < strlen; k++) { 206 char c = input.charAt(k); 207 208 output[offset + (2 * k)] = (byte) c; 209 output[offset + (2 * k) + 1] = (byte) (c >> 8); 210 } 211 } 212 213 /** 214 * Write uncompressed unicode 215 * 216 *@param input the String containing the unicode data to be written 217 *@param output the byte array to hold the uncompressed unicode 218 *@param offset the offset to start writing into the byte array 219 */ 220 221 public static void putUncompressedUnicodeHigh(final String input, 222 final byte[] output, 223 final int offset) { 224 int strlen = input.length(); 225 226 for (int k = 0; k < strlen; k++) { 227 char c = input.charAt(k); 228 229 output[offset + (2 * k)] = (byte) (c >> 8); 230 output[offset + (2 * k)] = (byte) c; 231 } 232 } 233 234 235 236 237 /** 238 * Description of the Method 239 * 240 *@param message Description of the Parameter 241 *@param params Description of the Parameter 242 *@return Description of the Return Value 243 */ 244 public static String format(String message, Object[] params) { 245 int currentParamNumber = 0; 246 StringBuffer formattedMessage = new StringBuffer(); 247 248 for (int i = 0; i < message.length(); i++) { 249 if (message.charAt(i) == '%') { 250 if (currentParamNumber >= params.length) { 251 formattedMessage.append("?missing data?"); 252 } else if ((params[currentParamNumber] instanceof Number) 253 && (i + 1 < message.length())) { 254 i += matchOptionalFormatting( 255 (Number) params[currentParamNumber++], 256 message.substring(i + 1), formattedMessage); 257 } else { 258 formattedMessage.append(params[currentParamNumber++].toString()); 259 } 260 } else { 261 if ((message.charAt(i) == '\\') && (i + 1 < message.length()) 262 && (message.charAt(i + 1) == '%')) { 263 formattedMessage.append('%'); 264 i++; 265 } else { 266 formattedMessage.append(message.charAt(i)); 267 } 268 } 269 } 270 return formattedMessage.toString(); 271 } 272 273 274 /** 275 * Description of the Method 276 * 277 *@param number Description of the Parameter 278 *@param formatting Description of the Parameter 279 *@param outputTo Description of the Parameter 280 *@return Description of the Return Value 281 */ 282 private static int matchOptionalFormatting(Number number, 283 String formatting, 284 StringBuffer outputTo) { 285 NumberFormat numberFormat = NumberFormat.getInstance(); 286 287 if ((0 < formatting.length()) 288 && Character.isDigit(formatting.charAt(0))) { 289 numberFormat.setMinimumIntegerDigits(Integer.parseInt(formatting.charAt(0) + "")); 290 if ((2 < formatting.length()) && (formatting.charAt(1) == '.') 291 && Character.isDigit(formatting.charAt(2))) { 292 numberFormat.setMaximumFractionDigits(Integer.parseInt(formatting.charAt(2) + "")); 293 numberFormat.format(number, outputTo, new FieldPosition(0)); 294 return 3; 295 } 296 numberFormat.format(number, outputTo, new FieldPosition(0)); 297 return 1; 298 } else if ((0 < formatting.length()) && (formatting.charAt(0) == '.')) { 299 if ((1 < formatting.length()) 300 && Character.isDigit(formatting.charAt(1))) { 301 numberFormat.setMaximumFractionDigits(Integer.parseInt(formatting.charAt(1) + "")); 302 numberFormat.format(number, outputTo, new FieldPosition(0)); 303 return 2; 304 } 305 } 306 numberFormat.format(number, outputTo, new FieldPosition(0)); 307 return 1; 308 } 309 } 310