1 package org.apache.maven.surefire.util.internal;
2
3 /*
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21
22 import java.util.StringTokenizer;
23
24 /**
25 * <p>
26 * Common <code>String</code> manipulation routines.
27 * </p>
28 * <p/>
29 * <p>
30 * Originally from <a href="http://jakarta.apache.org/turbine/">Turbine</a> and the GenerationJavaCore library.
31 * </p>
32 *
33 * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
34 * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
35 * @author <a href="mailto:gcoladonato@yahoo.com">Greg Coladonato</a>
36 * @author <a href="mailto:bayard@generationjava.com">Henri Yandell</a>
37 * @author <a href="mailto:ed@codehaus.org">Ed Korthof</a>
38 * @author <a href="mailto:rand_mcneely@yahoo.com">Rand McNeely</a>
39 * @author Stephen Colebourne
40 * @author <a href="mailto:fredrik@westermarck.com">Fredrik Westermarck</a>
41 * @author Holger Krauth
42 * @author <a href="mailto:alex@purpletech.com">Alexander Day Chaffee</a>
43 * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
44 * @version $Id: StringUtils.java 8001 2009-01-03 13:17:09Z vsiveton $
45 * @noinspection JavaDoc
46 * <p/>
47 * A quick borrow from plexus-utils by Kristian Rosenvold, to restore jdk1.3 compat Threw away all the
48 * unused stuff.
49 * <p/>
50 * NOTE: This class is not part of any api and is public purely for technical reasons !
51 * @since 1.0
52 */
53 public class StringUtils
54 {
55 private static final byte[] HEX_CHARS = new byte[] {
56 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
57 'A', 'B', 'C', 'D', 'E', 'F' };
58
59 public static String[] split( String text, String separator )
60 {
61 int max = -1;
62 StringTokenizer tok;
63 if ( separator == null )
64 {
65 // Null separator means we're using StringTokenizer's default
66 // delimiter, which comprises all whitespace characters.
67 tok = new StringTokenizer( text );
68 }
69 else
70 {
71 tok = new StringTokenizer( text, separator );
72 }
73
74 int listSize = tok.countTokens();
75 if ( ( max > 0 ) && ( listSize > max ) )
76 {
77 listSize = max;
78 }
79
80 String[] list = new String[listSize];
81 int i = 0;
82 int lastTokenBegin;
83 int lastTokenEnd = 0;
84 while ( tok.hasMoreTokens() )
85 {
86 if ( ( max > 0 ) && ( i == listSize - 1 ) )
87 {
88 // In the situation where we hit the max yet have
89 // tokens left over in our input, the last list
90 // element gets all remaining text.
91 String endToken = tok.nextToken();
92 lastTokenBegin = text.indexOf( endToken, lastTokenEnd );
93 list[i] = text.substring( lastTokenBegin );
94 break;
95 }
96 else
97 {
98 list[i] = tok.nextToken();
99 lastTokenBegin = text.indexOf( list[i], lastTokenEnd );
100 lastTokenEnd = lastTokenBegin + list[i].length();
101 }
102 i++;
103 }
104 return list;
105 }
106
107 /**
108 * <p>
109 * Checks if a (trimmed) String is <code>null</code> or blank.
110 * </p>
111 *
112 * @param str the String to check
113 * @return <code>true</code> if the String is <code>null</code>, or length zero once trimmed
114 */
115 public static boolean isBlank( String str )
116 {
117 return ( ( str == null ) || ( str.trim().length() == 0 ) );
118 }
119
120 /**
121 * Escape the specified string to a representation that only consists of nicely printable characters, without any
122 * newlines and without a comma.
123 * <p>
124 * The reverse-method is {@link #unescapeString(StringBuilder, CharSequence)}.
125 *
126 * @param target target string buffer. The required space will be up to {@code str.getBytes().length * 5} chars.
127 * @param str String to escape values in, may be {@code null}.
128 */
129 @SuppressWarnings( "checkstyle:magicnumber" )
130 public static void escapeToPrintable( StringBuilder target, CharSequence str )
131 {
132 if ( target == null )
133 {
134 throw new IllegalArgumentException( "The target buffer must not be null" );
135 }
136 if ( str == null )
137 {
138 return;
139 }
140
141 for ( int i = 0; i < str.length(); i++ )
142 {
143 char c = str.charAt( i );
144
145 // handle non-nicely printable chars and the comma
146 if ( c < 32 || c > 126 || c == '\\' || c == ',' )
147 {
148 target.append( '\\' );
149 target.append( (char) HEX_CHARS[( 0xF000 & c ) >> 12] );
150 target.append( (char) HEX_CHARS[( 0x0F00 & c ) >> 8] );
151 target.append( (char) HEX_CHARS[( 0x00F0 & c ) >> 4] );
152 target.append( (char) HEX_CHARS[( 0x000F & c )] );
153 }
154 else
155 {
156 target.append( c );
157 }
158 }
159 }
160
161 /**
162 * Reverses the effect of {@link #escapeToPrintable(StringBuilder, CharSequence)}.
163 *
164 * @param target target string buffer
165 * @param str the String to un-escape, as created by {@link #escapeToPrintable(StringBuilder, CharSequence)}
166 */
167 public static void unescapeString( StringBuilder target, CharSequence str )
168 {
169 if ( target == null )
170 {
171 throw new IllegalArgumentException( "The target buffer must not be null" );
172 }
173 if ( str == null )
174 {
175 return;
176 }
177
178 for ( int i = 0; i < str.length(); i++ )
179 {
180 char ch = str.charAt( i );
181
182 if ( ch == '\\' )
183 {
184 target.append( (char) (
185 digit( str.charAt( ++i ) ) << 12
186 | digit( str.charAt( ++i ) ) << 8
187 | digit( str.charAt( ++i ) ) << 4
188 | digit( str.charAt( ++i ) )
189 ) );
190 }
191 else
192 {
193 target.append( ch );
194 }
195 }
196 }
197
198 private static int digit( char ch )
199 {
200 if ( ch >= 'a' )
201 {
202 return 10 + ch - 'a';
203 }
204 else if ( ch >= 'A' )
205 {
206 return 10 + ch - 'A';
207 }
208 else
209 {
210 return ch - '0';
211 }
212 }
213
214 /**
215 * Escapes the bytes in the array {@code str} to contain only 'printable' bytes.
216 * <p>
217 * Escaping is done by encoding the non-nicely printable bytes to {@code '\' + upperCaseHexBytes(byte)}.
218 * <p>
219 * A save length of {@code out} is {@code len * 3 + outoff}.
220 * <p>
221 * The reverse-method is {@link #unescapeBytes(byte[], String)}.
222 *
223 * @param out output buffer
224 * @param outoff offset in the output buffer
225 * @param input input buffer
226 * @param off offset in the input buffer
227 * @param len number of bytes to copy from the input buffer
228 * @return number of bytes written to {@code out}
229 */
230 @SuppressWarnings( "checkstyle:magicnumber" )
231 public static int escapeBytesToPrintable( byte[] out, int outoff, byte[] input, int off, int len )
232 {
233 if ( out == null )
234 {
235 throw new IllegalArgumentException( "The output array must not be null" );
236 }
237 if ( input == null || input.length == 0 )
238 {
239 return 0;
240 }
241 int outputPos = outoff;
242 int end = off + len;
243 for ( int i = off; i < end; i++ )
244 {
245 byte b = input[i];
246
247 // handle non-nicely printable bytes
248 if ( b < 32 || b > 126 || b == '\\' || b == ',' )
249 {
250 int upper = ( 0xF0 & b ) >> 4;
251 int lower = ( 0x0F & b );
252 out[outputPos++] = '\\';
253 out[outputPos++] = HEX_CHARS[upper];
254 out[outputPos++] = HEX_CHARS[lower];
255 }
256 else
257 {
258 out[outputPos++] = b;
259 }
260 }
261
262 return outputPos - outoff;
263 }
264
265 /**
266 * Reverses the effect of {@link #escapeBytesToPrintable(byte[], int, byte[], int, int)}.
267 * <p>
268 * A save length of {@code out} is {@code str.length()}
269 *
270 * @param out the target byte array
271 * @param str the input String
272 * @return the number of bytes written to {@code out}
273 */
274 public static int unescapeBytes( byte[] out, String str )
275 {
276 int outPos = 0;
277 if ( out == null )
278 {
279 throw new IllegalArgumentException( "The output array must not be null" );
280 }
281 if ( str == null )
282 {
283 return 0;
284 }
285 for ( int i = 0; i < str.length(); i++ )
286 {
287 char ch = str.charAt( i );
288
289 if ( ch == '\\' )
290 {
291 int upper = digit( str.charAt( ++i ) );
292 int lower = digit( str.charAt( ++i ) );
293 out[outPos++] = (byte) ( upper << 4 | lower );
294 }
295 else
296 {
297 out[outPos++] = (byte) ch;
298 }
299 }
300 return outPos;
301 }
302 }