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