1 | package org.farng.mp3; |
2 | |
3 | import java.io.BufferedInputStream; |
4 | import java.io.BufferedOutputStream; |
5 | import java.io.File; |
6 | import java.io.FileInputStream; |
7 | import java.io.FileNotFoundException; |
8 | import java.io.FileOutputStream; |
9 | import java.io.IOException; |
10 | import java.lang.reflect.Constructor; |
11 | import java.util.HashMap; |
12 | import java.util.Iterator; |
13 | import java.util.Map; |
14 | import java.util.Stack; |
15 | import java.util.StringTokenizer; |
16 | |
17 | /** |
18 | * This is a holder class that contains static methods that I use in my library. They may or may not be useful for |
19 | * anyone else extending the library. |
20 | * |
21 | * @author Eric Farng |
22 | * @version $Revision: 1.4 $ |
23 | */ |
24 | public class TagUtility { |
25 | |
26 | /** |
27 | * integer difference between ASCII 'A' and ASCII 'a' |
28 | */ |
29 | private static final int UPPERCASE; |
30 | /** |
31 | * Convenience <code>HashMap</code> to help fix capitilization of words. It maps all words in |
32 | * <code>TagConstants.upperLowerCase</code> from all lower case to their desired capitilziation. |
33 | */ |
34 | private static final Map capitalizationMap; |
35 | |
36 | static { |
37 | UPPERCASE = (int) 'A' - (int) 'a'; |
38 | capitalizationMap = new HashMap(32); |
39 | final Iterator iterator = TagOptionSingleton.getInstance().getUpperLowerCaseWordListIterator(); |
40 | while (iterator.hasNext()) { |
41 | final String word = (String) iterator.next(); |
42 | capitalizationMap.put(word.toLowerCase(), word); |
43 | } |
44 | } |
45 | |
46 | /** |
47 | * Creates a new TagUtility object. |
48 | */ |
49 | private TagUtility() { |
50 | super(); |
51 | } |
52 | |
53 | /** |
54 | * Given an ID, get the ID3v2 frame description or the Lyrics3 field description. This takes any kind of ID (four or |
55 | * three letter ID3v2 IDs, and three letter Lyrics3 IDs) |
56 | * |
57 | * @param identifier frame identifier |
58 | * |
59 | * @return frame description |
60 | */ |
61 | public static String getFrameDescription(final String identifier) { |
62 | if (identifier == null) { |
63 | throw new NullPointerException("Identifier is null"); |
64 | } |
65 | String returnValue = null; |
66 | if (identifier.length() > 2) { |
67 | if (identifier.length() == 4) { |
68 | final String idPrefix = identifier.substring(0, 4); |
69 | returnValue = (String) TagConstant.id3v2_4FrameIdToString.get(idPrefix); |
70 | if (returnValue == null) { |
71 | returnValue = (String) TagConstant.id3v2_3FrameIdToString.get(idPrefix); |
72 | } |
73 | } |
74 | if (returnValue == null) { |
75 | returnValue = (String) TagConstant.id3v2_2FrameIdToString.get(identifier.substring(0, 3)); |
76 | } |
77 | if (returnValue == null) { |
78 | returnValue = (String) TagConstant.lyrics3v2FieldIdToString.get(identifier.substring(0, 3)); |
79 | } |
80 | } |
81 | return returnValue; |
82 | } |
83 | |
84 | /** |
85 | * Returns true if the identifier is a valid ID3v2.2 frame identifier |
86 | * |
87 | * @param identifier string to test |
88 | * |
89 | * @return true if the identifier is a valid ID3v2.2 frame identifier |
90 | */ |
91 | public static boolean isID3v2_2FrameIdentifier(final String identifier) { |
92 | if (identifier == null) { |
93 | throw new NullPointerException("Identifier is null"); |
94 | } |
95 | if (identifier.length() < 3) { |
96 | return false; |
97 | } else if (identifier.length() == 3) { |
98 | return TagConstant.id3v2_2FrameIdToString.containsKey(identifier); |
99 | } else { |
100 | final String upperIdentifier = identifier.toUpperCase(); |
101 | if (upperIdentifier.charAt(3) >= 'A' && upperIdentifier.charAt(3) <= 'Z') { |
102 | return TagConstant.id3v2_2FrameIdToString.containsKey(upperIdentifier.substring(0, 4)); |
103 | } |
104 | return TagConstant.id3v2_2FrameIdToString.containsKey(upperIdentifier.subSequence(0, 3)); |
105 | } |
106 | } |
107 | |
108 | /** |
109 | * Returns true if the identifier is a valid ID3v2.3 frame identifier |
110 | * |
111 | * @param identifier string to test |
112 | * |
113 | * @return true if the identifier is a valid ID3v2.3 frame identifier |
114 | */ |
115 | public static boolean isID3v2_3FrameIdentifier(final String identifier) { |
116 | if (identifier == null) { |
117 | throw new NullPointerException("Identifier is null"); |
118 | } |
119 | if (identifier.length() < 4) { |
120 | return false; |
121 | } |
122 | return TagConstant.id3v2_3FrameIdToString.containsKey(identifier.substring(0, 4)); |
123 | } |
124 | |
125 | /** |
126 | * Returns true if the identifier is a valid ID3v2.4 frame identifier |
127 | * |
128 | * @param identifier string to test |
129 | * |
130 | * @return true if the identifier is a valid ID3v2.4 frame identifier |
131 | */ |
132 | public static boolean isID3v2_4FrameIdentifier(final String identifier) { |
133 | if (identifier == null) { |
134 | throw new NullPointerException("Identifier is null"); |
135 | } |
136 | if (identifier.length() < 4) { |
137 | return false; |
138 | } |
139 | return TagConstant.id3v2_4FrameIdToString.containsKey(identifier.substring(0, 4)); |
140 | } |
141 | |
142 | /** |
143 | * Returns true if the identifier is a valid Lyrics3v2 frame identifier |
144 | * |
145 | * @param identifier string to test |
146 | * |
147 | * @return true if the identifier is a valid Lyrics3v2 frame identifier |
148 | */ |
149 | public static boolean isLyrics3v2FieldIdentifier(final String identifier) { |
150 | if (identifier == null) { |
151 | throw new NullPointerException("Identifier is null"); |
152 | } |
153 | if (identifier.length() < 3) { |
154 | return false; |
155 | } |
156 | return TagConstant.lyrics3v2FieldIdToString.containsKey(identifier.substring(0, 3)); |
157 | } |
158 | |
159 | /** |
160 | * Returns true if the string has matching parenthesis. This method matches all four parenthesis and also enclosed |
161 | * parenthesis. |
162 | * |
163 | * @param str string to test |
164 | * |
165 | * @return true if the string has matching parenthesis |
166 | */ |
167 | public static boolean isMatchingParenthesis(final String str) { |
168 | if (str == null) { |
169 | throw new NullPointerException("String is null"); |
170 | } |
171 | final TagOptionSingleton option = TagOptionSingleton.getInstance(); |
172 | final int length = str.length(); |
173 | for (int i = 0; i < length; i++) { |
174 | final char ch = str.charAt(i); |
175 | if (option.isCloseParenthesis(Character.toString(ch))) { |
176 | return false; |
177 | } |
178 | if (option.isOpenParenthesis(Character.toString(ch))) { |
179 | i = findMatchingParenthesis(str, i); |
180 | if (i < 0) { |
181 | return false; |
182 | } |
183 | } |
184 | } |
185 | return true; |
186 | } |
187 | |
188 | /** |
189 | * Given an object, try to return it as a <code>long</code>. This tries to parse a string, and takes <code>Long, |
190 | * Short, Byte, Integer</code> objects and gets their value. An exception is not explicityly thrown here because it |
191 | * would causes too many other methods to also throw it. |
192 | * |
193 | * @param value object to find long from. |
194 | * |
195 | * @return <code>long</code> value |
196 | */ |
197 | public static long getWholeNumber(final Object value) { |
198 | if (value == null) { |
199 | throw new NullPointerException("Value is null"); |
200 | } |
201 | final long number; |
202 | if (value instanceof String) { |
203 | number = Long.parseLong((String) value); |
204 | } else if (value instanceof Byte) { |
205 | number = ((Byte) value).byteValue(); |
206 | } else if (value instanceof Short) { |
207 | number = ((Short) value).shortValue(); |
208 | } else if (value instanceof Integer) { |
209 | number = ((Integer) value).intValue(); |
210 | } else if (value instanceof Long) { |
211 | number = ((Long) value).longValue(); |
212 | } else { |
213 | throw new IllegalArgumentException("Unsupported value class: " + value.getClass().getName()); |
214 | } |
215 | return number; |
216 | } |
217 | |
218 | /** |
219 | * Add a timestamp string to a given string. This is used in the GUI and I'm not sure why it is defined here. |
220 | * |
221 | * @param text textarea string to insert to |
222 | * @param origPos current position of the cursor |
223 | * |
224 | * @return new string to use in the text area |
225 | */ |
226 | public static String addTimeStampToTextArea(final String text, final int origPos) { |
227 | //todo move this to a GUI class |
228 | //todo fix the case of adding time stamp to EOLN, EOLN, EOF (adding |
229 | final String newText; |
230 | if (text.length() == 0) { // special empty case |
231 | newText = "[00:00]"; |
232 | } else { |
233 | int i = origPos; |
234 | i = Math.min(i, text.length() - 1); // if at end of whole string |
235 | if (text.charAt(i) == '\n') { |
236 | i--; // if at the end of line |
237 | } |
238 | for (; i > 0; i--) { |
239 | if (text.charAt(i) == '\n') { |
240 | break; |
241 | } |
242 | } |
243 | if (i == 0) { // if at very first character |
244 | newText = "[00:00]" + text; |
245 | } else { |
246 | i++; |
247 | final String before = text.substring(0, i); |
248 | final String after = text.substring(i); |
249 | newText = before + "[00:00]" + after; |
250 | } |
251 | } |
252 | return newText; |
253 | } |
254 | |
255 | public static String appendBeforeExtension(final String filename, final String addition) { |
256 | if (addition == null) { |
257 | return filename; |
258 | } |
259 | if (filename == null) { |
260 | return addition; |
261 | } |
262 | final int index = filename.lastIndexOf('.'); |
263 | if (index < 0) { |
264 | return filename + addition; |
265 | } |
266 | return filename.substring(0, index) + addition + filename.substring(index); |
267 | } |
268 | |
269 | public static String convertFrameID2_2to2_3(final String identifier) { |
270 | if (identifier == null) { |
271 | throw new NullPointerException("Identifier is null"); |
272 | } |
273 | if (identifier.length() < 3) { |
274 | return null; |
275 | } |
276 | return (String) TagConstant.id3v2_2ToId3v2_3.get(identifier.subSequence(0, 3)); |
277 | } |
278 | |
279 | public static String convertFrameID2_2to2_4(final String identifier) { |
280 | if (identifier == null) { |
281 | throw new NullPointerException("Identifier is null"); |
282 | } |
283 | if (identifier.length() < 3) { |
284 | return null; |
285 | } |
286 | String id = (String) TagConstant.id3v2_2ToId3v2_3.get(identifier.substring(0, 3)); |
287 | if (id != null) { |
288 | id = (String) TagConstant.id3v2_3ToId3v2_4.get(id); |
289 | } |
290 | return id; |
291 | } |
292 | |
293 | public static String convertFrameID2_3to2_2(final String identifier) { |
294 | if (identifier == null) { |
295 | throw new NullPointerException("Identifier is null"); |
296 | } |
297 | if (identifier.length() < 4) { |
298 | return null; |
299 | } |
300 | return (String) TagConstant.id3v2_3ToId3v2_2.get(identifier.substring(0, 4)); |
301 | } |
302 | |
303 | public static String convertFrameID2_3to2_4(final String identifier) { |
304 | if (identifier == null) { |
305 | throw new NullPointerException("Identifier is null"); |
306 | } |
307 | if (identifier.length() < 4) { |
308 | return null; |
309 | } |
310 | return (String) TagConstant.id3v2_3ToId3v2_4.get(identifier.substring(0, 4)); |
311 | } |
312 | |
313 | public static String convertFrameID2_4to2_2(final String identifier) { |
314 | if (identifier == null) { |
315 | throw new NullPointerException("Identifier is null"); |
316 | } |
317 | if (identifier.length() < 4) { |
318 | return null; |
319 | } |
320 | String id = (String) TagConstant.id3v2_4ToId3v2_3.get(identifier.substring(0, 4)); |
321 | if (id != null) { |
322 | id = (String) TagConstant.id3v2_3ToId3v2_2.get(id); |
323 | } |
324 | return id; |
325 | } |
326 | |
327 | public static String convertFrameID2_4to2_3(final String identifier) { |
328 | if (identifier == null) { |
329 | throw new NullPointerException("Identifier is null"); |
330 | } |
331 | if (identifier.length() < 4) { |
332 | return null; |
333 | } |
334 | return (String) TagConstant.id3v2_4ToId3v2_3.get(identifier); |
335 | } |
336 | |
337 | /** |
338 | * Copy the source file to the destination file. The destination file will be deleted first before copying starts. |
339 | */ |
340 | public static void copyFile(final File source, final File destination) throws FileNotFoundException, IOException { |
341 | if (source == null) { |
342 | throw new NullPointerException("Source is null"); |
343 | } |
344 | if (destination == null) { |
345 | throw new NullPointerException("Destination is null"); |
346 | } |
347 | if (source.exists() == false) { |
348 | throw new NullPointerException("Source file not found."); |
349 | } |
350 | FileInputStream fio = null; |
351 | BufferedInputStream bio = null; |
352 | FileOutputStream fos = null; |
353 | BufferedOutputStream bos = null; |
354 | final byte[] buffer; |
355 | try { |
356 | if (destination.exists()) { |
357 | destination.delete(); |
358 | } |
359 | fio = new FileInputStream(source); |
360 | bio = new BufferedInputStream(fio); |
361 | fos = new FileOutputStream(destination); |
362 | bos = new BufferedOutputStream(fos); |
363 | buffer = new byte[1024]; |
364 | int b = bio.read(buffer); |
365 | while (b != -1) { |
366 | bos.write(buffer, 0, b); |
367 | b = bio.read(buffer); |
368 | } |
369 | } finally { |
370 | if (bio != null) { |
371 | bio.close(); |
372 | } |
373 | if (bos != null) { |
374 | bos.flush(); |
375 | bos.close(); |
376 | } |
377 | if (fos != null) { |
378 | fos.close(); |
379 | } |
380 | if (fio != null) { |
381 | fio.close(); |
382 | } |
383 | } |
384 | } |
385 | |
386 | /** |
387 | * Unable to instantiate abstract classes, so can't call the copy constructor. So find out the instianted class name |
388 | * and call the copy constructor through reflection. |
389 | */ |
390 | public static Object copyObject(final Object copyObject) { |
391 | final Constructor constructor; |
392 | final Class[] constructorParameterArray; |
393 | final Object[] parameterArray; |
394 | if (copyObject == null) { |
395 | return null; |
396 | } |
397 | try { |
398 | constructorParameterArray = new Class[1]; |
399 | constructorParameterArray[0] = copyObject.getClass(); |
400 | constructor = copyObject.getClass().getConstructor(constructorParameterArray); |
401 | parameterArray = new Object[1]; |
402 | parameterArray[0] = copyObject; |
403 | return constructor.newInstance(parameterArray); |
404 | } catch (NoSuchMethodException ex) { |
405 | throw new IllegalArgumentException("NoSuchMethodException: Error finding constructor to create copy"); |
406 | } catch (IllegalAccessException ex) { |
407 | throw new IllegalArgumentException("IllegalAccessException: No access to run constructor to create copy"); |
408 | } catch (InstantiationException ex) { |
409 | throw new IllegalArgumentException("InstantiationException: Unable to instantiate constructor to copy"); |
410 | } catch (java.lang.reflect.InvocationTargetException ex) { |
411 | throw new IllegalArgumentException("InvocationTargetException: Unable to invoke constructor to create copy"); |
412 | } |
413 | } |
414 | |
415 | /** |
416 | * return the index of the matching of parenthesis. This will match all four parenthesis and enclosed parenthesis. |
417 | * |
418 | * @param str string to search |
419 | * @param index index of string to start searching. This index should point to the opening parenthesis. |
420 | * |
421 | * @return index of the matching parenthesis. -1 is returned if none is found, or if the parenthesis are |
422 | * unbalanced. |
423 | */ |
424 | public static int findMatchingParenthesis(final String str, final int index) { |
425 | if (str == null) { |
426 | throw new NullPointerException("String is null"); |
427 | } |
428 | if ((index < 0) || (index >= str.length())) { |
429 | throw new IndexOutOfBoundsException("Index to image string is out of bounds: offset = " + |
430 | index + |
431 | ", string.length()" + |
432 | str.length()); |
433 | } |
434 | final TagOptionSingleton option = TagOptionSingleton.getInstance(); |
435 | final Stack stack = new Stack(); |
436 | String chString; |
437 | String open; |
438 | char ch; |
439 | if (index >= 0) { |
440 | final int length = str.length(); |
441 | if (length == 0) { |
442 | return 0; |
443 | } |
444 | for (int i = index; i < length; i++) { |
445 | ch = str.charAt(i); |
446 | chString = ch + ""; |
447 | if (option.isOpenParenthesis(chString)) { |
448 | stack.push(chString); |
449 | } |
450 | if (option.isCloseParenthesis(chString)) { |
451 | if (stack.size() <= 0) { |
452 | return -1; |
453 | } |
454 | open = (String) stack.pop(); |
455 | if (option.getCloseParenthesis(open).equals(chString) == false) { |
456 | return -1; |
457 | } |
458 | } |
459 | if (stack.size() <= 0) { |
460 | return i; |
461 | } |
462 | } |
463 | } |
464 | return -1; |
465 | } |
466 | |
467 | /** |
468 | * Find the first whole number that can be parsed from the string |
469 | * |
470 | * @param str string to search |
471 | * |
472 | * @return first whole number that can be parsed from the string |
473 | */ |
474 | public static long findNumber(final String str) throws TagException { |
475 | return findNumber(str, 0); |
476 | } |
477 | |
478 | /** |
479 | * Find the first whole number that can be parsed from the string |
480 | * |
481 | * @param str string to search |
482 | * @param offset start seaching from this index |
483 | * |
484 | * @return first whole number that can be parsed from the string |
485 | */ |
486 | public static long findNumber(final String str, final int offset) throws TagException { |
487 | if (str == null) { |
488 | throw new NullPointerException("String is null"); |
489 | } |
490 | if ((offset < 0) || (offset >= str.length())) { |
491 | throw new IndexOutOfBoundsException("Offset to image string is out of bounds: offset = " + |
492 | offset + |
493 | ", string.length()" + |
494 | str.length()); |
495 | } |
496 | int i; |
497 | int j; |
498 | final long num; |
499 | i = offset; |
500 | while (i < str.length()) { |
501 | if (((str.charAt(i) >= '0') && (str.charAt(i) <= '9')) || (str.charAt(i) == '-')) { |
502 | break; |
503 | } |
504 | i++; |
505 | } |
506 | j = i + 1; |
507 | while (j < str.length()) { |
508 | if (((str.charAt(j) < '0') || (str.charAt(j) > '9'))) { |
509 | break; |
510 | } |
511 | j++; |
512 | } |
513 | if ((j <= str.length()) && (j > i)) { |
514 | num = Long.parseLong(str.substring(i, j)); |
515 | } else { |
516 | throw new TagException("Unable to find integer in string: " + str); |
517 | } |
518 | return num; |
519 | } |
520 | |
521 | /** |
522 | * String formatting function to pad the given string with the given character |
523 | * |
524 | * @param str string to pad |
525 | * @param length total length of new string |
526 | * @param ch character to pad the string with |
527 | * @param padBefore if true, add the padding at the start of the string. if false, add the padding at the end of the |
528 | * string. |
529 | * |
530 | * @return new padded string. |
531 | */ |
532 | public static String padString(final String str, final int length, final char ch, final boolean padBefore) { |
533 | if (length < 0) { |
534 | return str; |
535 | } else if (length == 0) { |
536 | if (str == null) { |
537 | return ""; |
538 | } |
539 | return str; |
540 | } |
541 | int strLength = 0; |
542 | if (str != null) { |
543 | strLength = str.length(); |
544 | } |
545 | if (strLength >= length) { |
546 | return str; |
547 | } |
548 | final char[] buffer = new char[length]; |
549 | int next = 0; |
550 | if (padBefore) { |
551 | for (int i = 0; i < (length - strLength); i++) { |
552 | buffer[next++] = ch; |
553 | } |
554 | } |
555 | if (str != null) { |
556 | for (int i = 0; i < strLength; i++) { |
557 | buffer[next++] = str.charAt(i); |
558 | } |
559 | } |
560 | if (padBefore == false) { |
561 | for (int i = 0; i < (length - strLength); i++) { |
562 | buffer[next++] = ch; |
563 | } |
564 | } |
565 | return new String(buffer); |
566 | } |
567 | |
568 | /** |
569 | * Replace the Unix end of line character with the DOS end of line character. |
570 | * |
571 | * @param text string to search and replace |
572 | * |
573 | * @return replaced string |
574 | */ |
575 | public static String replaceEOLNwithCRLF(final String text) { |
576 | String newText = null; |
577 | if (text != null) { |
578 | newText = ""; |
579 | int oldPos = 0; |
580 | int newPos = text.indexOf('\n'); |
581 | while (newPos >= 0) { |
582 | newText += (text.substring(oldPos, newPos) + TagConstant.SEPERATOR_LINE); |
583 | oldPos = newPos + 1; |
584 | newPos = text.indexOf('\n', oldPos); |
585 | } |
586 | newText += text.substring(oldPos); |
587 | } |
588 | return newText; |
589 | } |
590 | |
591 | /** |
592 | * Search the <code>source</code> string for any occurance of <code>oldString</code> and replaced them all with |
593 | * <code>newString</code>. This searches for the entire word of old string. A blank space is appended to the front |
594 | * and back of <code>oldString</code> |
595 | */ |
596 | public static String replaceWord(String source, final String oldString, String newString) { |
597 | if (source == null) { |
598 | throw new NullPointerException("Source is null"); |
599 | } |
600 | if (oldString == null) { |
601 | throw new NullPointerException("Old string (string to be replaced) is null"); |
602 | } |
603 | if ((source.length() > 0) && (oldString.length() > 0)) { |
604 | if (newString == null) { |
605 | newString = ""; |
606 | } |
607 | final StringBuffer str = new StringBuffer(source); |
608 | int index = str.indexOf(oldString); |
609 | final int length = oldString.length(); |
610 | while (index >= 0) { |
611 | if (((index == 0) && Character.isWhitespace(str.charAt(index + length))) || |
612 | (Character.isWhitespace(str.charAt(index - 1)) && ((index + length) >= str.length())) || |
613 | (Character.isWhitespace(str.charAt(index - 1)) && |
614 | Character.isWhitespace(str.charAt(index + length)))) { |
615 | str.replace(index, index + length, newString); |
616 | } |
617 | index = str.indexOf(oldString, index); |
618 | } |
619 | source = str.toString(); |
620 | } |
621 | return source; |
622 | } |
623 | |
624 | /** |
625 | * Remove all occurances of the given character from the string argument. |
626 | * |
627 | * @param str String to search |
628 | * @param ch character to remove |
629 | * |
630 | * @return new String without the given charcter |
631 | */ |
632 | public static String stripChar(final String str, final char ch) { |
633 | if (str == null) { |
634 | throw new NullPointerException("String is null"); |
635 | } |
636 | final char[] buffer = new char[str.length()]; |
637 | int next = 0; |
638 | for (int i = 0; i < str.length(); i++) { |
639 | if (str.charAt(i) != ch) { |
640 | buffer[next++] = str.charAt(i); |
641 | } |
642 | } |
643 | return new String(buffer, 0, next); |
644 | } |
645 | |
646 | /** |
647 | * Change the given string into sentence case. Sentence case has the first words always capitalized. Any words in |
648 | * <code>TagConstants.upperLowerCase</code> will be capitalized that way. Any other words will be turned lower |
649 | * case. |
650 | * |
651 | * @param str String to modify |
652 | * @param keepUppercase if true, keep a word if it is already all in UPPERCASE |
653 | * |
654 | * @return new string in sentence case. |
655 | */ |
656 | public static String toSentenceCase(final String str, final boolean keepUppercase) { |
657 | if (str == null) { |
658 | throw new NullPointerException("String is null"); |
659 | } |
660 | final StringTokenizer tokenizer = new StringTokenizer(str); |
661 | String token; |
662 | final int numberTokens = tokenizer.countTokens(); |
663 | int countedTokens = 0; |
664 | final StringBuffer newString = new StringBuffer(); |
665 | |
666 | // Capitalize first word of all sentences. |
667 | if (tokenizer.hasMoreTokens()) { |
668 | token = tokenizer.nextToken(); |
669 | newString.append(capitalizeWord(token, keepUppercase)); |
670 | newString.append(' '); |
671 | countedTokens++; |
672 | } |
673 | |
674 | // go through all remainder tokens |
675 | while (tokenizer.hasMoreTokens() && (countedTokens < numberTokens)) { |
676 | token = tokenizer.nextToken(); |
677 | countedTokens++; |
678 | if (capitalizationMap.containsKey(token.toLowerCase())) { |
679 | newString.append(capitalizationMap.get(token.toLowerCase())); |
680 | } else if (keepUppercase && token.toUpperCase().equals(token)) { |
681 | newString.append(token); |
682 | } else { |
683 | newString.append(token.toLowerCase()); |
684 | } |
685 | newString.append(' '); |
686 | } |
687 | |
688 | // remove trailing space |
689 | if (newString.length() > 0) { |
690 | newString.deleteCharAt(newString.length() - 1); |
691 | } |
692 | return newString.toString(); |
693 | } |
694 | |
695 | /** |
696 | * Change the given string to title case. The first and last words of the string are always capitilized. Any words |
697 | * in <code>TagConstants.upperLowerCase</code> will be capitalized that way. Any other words will be capitalized. |
698 | * |
699 | * @param str String to modify |
700 | * @param keepUppercase if true, keep a word if it is already all in UPPERCASE |
701 | * |
702 | * @return new capitlized string. |
703 | */ |
704 | public static String toTitleCase(final String str, final boolean keepUppercase) { |
705 | if (str == null) { |
706 | throw new NullPointerException("String is null"); |
707 | } |
708 | final StringTokenizer tokenizer = new StringTokenizer(str); |
709 | String token; |
710 | final int numberTokens = tokenizer.countTokens(); |
711 | int countedTokens = 0; |
712 | final StringBuffer newString = new StringBuffer(); |
713 | |
714 | // Capitalize first word of all titles. |
715 | if (tokenizer.hasMoreTokens()) { |
716 | token = tokenizer.nextToken(); |
717 | newString.append(capitalizeWord(token, keepUppercase)); |
718 | newString.append(' '); |
719 | countedTokens++; |
720 | } |
721 | |
722 | // go through all remainder tokens except last |
723 | while (tokenizer.hasMoreTokens() && (countedTokens < (numberTokens - 1))) { |
724 | token = tokenizer.nextToken(); |
725 | countedTokens++; |
726 | if (capitalizationMap.containsKey(token.toLowerCase())) { |
727 | newString.append(capitalizationMap.get(token.toLowerCase())); |
728 | } else { |
729 | newString.append(capitalizeWord(token, keepUppercase)); |
730 | } |
731 | newString.append(' '); |
732 | } |
733 | |
734 | // Capitalize last word of all titles. |
735 | if (tokenizer.hasMoreTokens()) { |
736 | token = tokenizer.nextToken(); |
737 | newString.append(capitalizeWord(token, keepUppercase)); |
738 | newString.append(' '); |
739 | } |
740 | |
741 | // remove trailing space |
742 | if (newString.length() > 0) { |
743 | newString.deleteCharAt(newString.length() - 1); |
744 | } |
745 | return newString.toString(); |
746 | } |
747 | |
748 | /** |
749 | * truncate a string if it longer than the argument |
750 | * |
751 | * @param str String to truncate |
752 | * @param len maximum desired length of new string |
753 | */ |
754 | public static String truncate(final String str, final int len) { |
755 | if (str == null) { |
756 | throw new NullPointerException("String is null"); |
757 | } |
758 | if (len < 0) { |
759 | throw new IndexOutOfBoundsException("Length is less than zero"); |
760 | } |
761 | if (str.length() > len) { |
762 | return str.substring(0, len); |
763 | } |
764 | return str.trim(); |
765 | } |
766 | |
767 | /** |
768 | * Capitalize the word with the first letter upper case and all others lower case. |
769 | * |
770 | * @param word word to capitalize. |
771 | * @param keepUppercase if true, keep a word if it is already all in UPPERCASE |
772 | * |
773 | * @return new capitalized word. |
774 | */ |
775 | private static StringBuffer capitalizeWord(String word, final boolean keepUppercase) { |
776 | if (word == null) { |
777 | return null; |
778 | } |
779 | final StringBuffer wordBuffer = new StringBuffer(); |
780 | int index = 0; |
781 | if (keepUppercase && word.toUpperCase().equals(word)) { |
782 | wordBuffer.append(word); |
783 | } else { |
784 | word = word.toLowerCase(); |
785 | final int len = word.length(); |
786 | char ch; |
787 | ch = word.charAt(index); |
788 | while (((ch < 'a') || (ch > 'z')) && (index < (len - 1))) { |
789 | ch = word.charAt(++index); |
790 | } |
791 | if (index < len) { |
792 | wordBuffer.append(word.substring(0, index)); |
793 | wordBuffer.append((char) (ch + UPPERCASE)); |
794 | wordBuffer.append(word.substring(index + 1)); |
795 | } else { |
796 | wordBuffer.append(word); |
797 | } |
798 | } |
799 | return wordBuffer; |
800 | } |
801 | } |