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