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 public static void escapeToPrintable( StringBuilder target, CharSequence str )
130 {
131 if ( target == null )
132 {
133 throw new IllegalArgumentException( "The target buffer must not be null" );
134 }
135 if ( str == null )
136 {
137 return;
138 }
139
140 for ( int i = 0; i < str.length(); i++ ) {
141 char c = str.charAt( i );
142
143 // handle non-nicely printable chars and the comma
144 if ( c < 32 || c > 126 || c == '\\' || c == ',' )
145 {
146 target.append( '\\' );
147 target.append( (char) HEX_CHARS[( 0xF000 & c ) >> 12] );
148 target.append( (char) HEX_CHARS[( 0x0F00 & c ) >> 8] );
149 target.append( (char) HEX_CHARS[( 0x00F0 & c ) >> 4] );
150 target.append( (char) HEX_CHARS[( 0x000F & c )] );
151 }
152 else
153 {
154 target.append( c );
155 }
156 }
157 }
158
159 /**
160 * Reverses the effect of {@link #escapeToPrintable(StringBuilder, CharSequence)}.
161 *
162 * @param target target string buffer
163 * @param str the String to un-escape, as created by {@link #escapeToPrintable(StringBuilder, CharSequence)}
164 */
165 public static void unescapeString( StringBuilder target, CharSequence str )
166 {
167 if ( target == null )
168 {
169 throw new IllegalArgumentException( "The target buffer must not be null" );
170 }
171 if ( str == null )
172 {
173 return;
174 }
175
176 for ( int i = 0; i < str.length(); i++ )
177 {
178 char ch = str.charAt( i );
179
180 if ( ch == '\\' )
181 {
182 target.append( (char) (
183 digit( str.charAt( ++i ) ) << 12
184 | digit( str.charAt( ++i ) ) << 8
185 | digit( str.charAt( ++i ) ) << 4
186 | digit( str.charAt( ++i ) )
187 ) );
188 }
189 else
190 {
191 target.append( ch );
192 }
193 }
194 }
195
196 private static int digit( char ch )
197 {
198 if ( ch >= 'a' )
199 {
200 return 10 + ch - 'a';
201 }
202 else if ( ch >= 'A' )
203 {
204 return 10 + ch - 'A';
205 }
206 else
207 {
208 return ch - '0';
209 }
210 }
211
212 /**
213 * Escapes the bytes in the array {@code str} to contain only 'printable' bytes.
214 * <p>
215 * Escaping is done by encoding the non-nicely printable bytes to {@code '\' + upperCaseHexBytes(byte)}.
216 * <p>
217 * A save length of {@code out} is {@code len * 3 + outoff}.
218 * <p>
219 * The reverse-method is {@link #unescapeBytes(byte[], String)}.
220 *
221 * @param out output buffer
222 * @param outoff offset in the output buffer
223 * @param input input buffer
224 * @param off offset in the input buffer
225 * @param len number of bytes to copy from the input buffer
226 * @return number of bytes written to {@code out}
227 */
228 public static int escapeBytesToPrintable( byte[] out, int outoff, byte[] input, int off, int len )
229 {
230 if ( out == null )
231 {
232 throw new IllegalArgumentException( "The output array must not be null" );
233 }
234 if ( input == null || input.length == 0 )
235 {
236 return 0;
237 }
238 int outputPos = outoff;
239 int end = off + len;
240 for ( int i = off; i < end; i++ )
241 {
242 byte b = input[i];
243
244 // handle non-nicely printable bytes
245 if ( b < 32 || b > 126 || b == '\\' || b == ',' )
246 {
247 int upper = ( 0xF0 & b ) >> 4;
248 int lower = ( 0x0F & b );
249 out[outputPos++] = '\\';
250 out[outputPos++] = HEX_CHARS[upper];
251 out[outputPos++] = HEX_CHARS[lower];
252 }
253 else
254 {
255 out[outputPos++] = b;
256 }
257 }
258
259 return outputPos - outoff;
260 }
261
262 /**
263 * Reverses the effect of {@link #escapeBytesToPrintable(byte[], int, byte[], int, int)}.
264 * <p>
265 * A save length of {@code out} is {@code str.length()}
266 *
267 * @param out the target byte array
268 * @param str the input String
269 * @return the number of bytes written to {@code out}
270 */
271 public static int unescapeBytes( byte[] out, String str )
272 {
273 int outPos = 0;
274 if ( out == null )
275 {
276 throw new IllegalArgumentException( "The output array must not be null" );
277 }
278 if ( str == null )
279 {
280 return 0;
281 }
282 for ( int i = 0; i < str.length(); i++ )
283 {
284 char ch = str.charAt( i );
285
286 if ( ch == '\\' )
287 {
288 int upper = digit( str.charAt( ++i ) );
289 int lower = digit( str.charAt( ++i ) );
290 out[outPos++] = (byte) ( upper << 4 | lower );
291 }
292 else
293 {
294 out[outPos++] = (byte) ch;
295 }
296 }
297 return outPos;
298 }
299 }