001 //$HeadURL: http://svn.wald.intevation.org/svn/deegree/base/trunk/src/org/deegree/framework/util/StringTools.java $ 002 /*---------------------------------------------------------------------------- 003 This file is part of deegree, http://deegree.org/ 004 Copyright (C) 2001-2009 by: 005 Department of Geography, University of Bonn 006 and 007 lat/lon GmbH 008 009 This library is free software; you can redistribute it and/or modify it under 010 the terms of the GNU Lesser General Public License as published by the Free 011 Software Foundation; either version 2.1 of the License, or (at your option) 012 any later version. 013 This library is distributed in the hope that it will be useful, but WITHOUT 014 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 015 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 016 details. 017 You should have received a copy of the GNU Lesser General Public License 018 along with this library; if not, write to the Free Software Foundation, Inc., 019 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 020 021 Contact information: 022 023 lat/lon GmbH 024 Aennchenstr. 19, 53177 Bonn 025 Germany 026 http://lat-lon.de/ 027 028 Department of Geography, University of Bonn 029 Prof. Dr. Klaus Greve 030 Postfach 1147, 53001 Bonn 031 Germany 032 http://www.geographie.uni-bonn.de/deegree/ 033 034 e-mail: info@deegree.org 035 ----------------------------------------------------------------------------*/ 036 package org.deegree.framework.util; 037 038 import java.io.BufferedReader; 039 import java.io.IOException; 040 import java.io.InputStream; 041 import java.io.InputStreamReader; 042 import java.io.PrintStream; 043 import java.io.StringReader; 044 import java.util.ArrayList; 045 import java.util.HashMap; 046 import java.util.Iterator; 047 import java.util.List; 048 import java.util.Locale; 049 import java.util.Map; 050 import java.util.Set; 051 import java.util.StringTokenizer; 052 053 import org.deegree.framework.xml.XMLFragment; 054 import org.deegree.framework.xml.XMLParsingException; 055 import org.deegree.framework.xml.XMLTools; 056 import org.deegree.ogcbase.CommonNamespaces; 057 import org.w3c.dom.Node; 058 import org.xml.sax.SAXException; 059 060 /** 061 * this is a collection of some methods that extends the functionality of the sun-java string class. 062 * 063 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> 064 * @author last edited by: $Author: apoth $ 065 * 066 * @version $Revision: 30474 $, $Date: 2011-04-17 11:23:29 +0200 (Sun, 17 Apr 2011) $ 067 */ 068 public class StringTools { 069 070 /** 071 * This map is used for methods normalizeString() and initMap(). 072 * 073 * key = locale language, e.g. "de" value = map of substitution rules for this locale 074 */ 075 private static Map<String, Map<String, String>> localeMap; 076 077 /** 078 * concatenates an array of strings using a 079 * 080 * @see StringBuffer 081 * 082 * @param size 083 * estimated size of the target string 084 * @param objects 085 * toString() will be called for each object to append it to the result string 086 * @return the concatenated String 087 */ 088 public static String concat( int size, Object... objects ) { 089 StringBuilder sbb = new StringBuilder( size ); 090 for ( int i = 0; i < objects.length; i++ ) { 091 sbb.append( objects[i] ); 092 } 093 return sbb.toString(); 094 } 095 096 /** 097 * replaces occurences of a string fragment within a string by a new string. 098 * 099 * @param target 100 * is the original string 101 * @param from 102 * is the string to be replaced 103 * @param to 104 * is the string which will used to replace 105 * @param all 106 * if it's true all occurences of the string to be replaced will be replaced. else only the first 107 * occurence will be replaced. 108 * @return the changed target string 109 */ 110 public static String replace( String target, String from, String to, boolean all ) { 111 112 StringBuffer buffer = new StringBuffer( target.length() ); 113 int copyFrom = 0; 114 char[] targetChars = null; 115 int lf = from.length(); 116 int start = -1; 117 do { 118 start = target.indexOf( from ); 119 copyFrom = 0; 120 if ( start == -1 ) { 121 return target; 122 } 123 124 targetChars = target.toCharArray(); 125 while ( start != -1 ) { 126 buffer.append( targetChars, copyFrom, start - copyFrom ); 127 buffer.append( to ); 128 copyFrom = start + lf; 129 start = target.indexOf( from, copyFrom ); 130 if ( !all ) { 131 start = -1; 132 } 133 } 134 buffer.append( targetChars, copyFrom, targetChars.length - copyFrom ); 135 target = buffer.toString(); 136 buffer.delete( 0, buffer.length() ); 137 } while ( target.indexOf( from ) > -1 && to.indexOf( from ) < 0 ); 138 139 return target; 140 } 141 142 /** 143 * parse a string and return its tokens as array 144 * 145 * @param s 146 * string to parse 147 * @param delimiter 148 * delimiter that marks the end of a token 149 * @param deleteDoubles 150 * if it's true all string that are already within the resulting array will be deleted, so that there 151 * will only be one copy of them. 152 * @return an Array of Strings 153 */ 154 public static String[] toArray( String s, String delimiter, boolean deleteDoubles ) { 155 if ( s == null || s.equals( "" ) ) { 156 return new String[0]; 157 } 158 159 StringTokenizer st = new StringTokenizer( s, delimiter ); 160 ArrayList<String> vec = new ArrayList<String>( st.countTokens() ); 161 162 if ( st.countTokens() > 0 ) { 163 for ( int i = 0; st.hasMoreTokens(); i++ ) { 164 String t = st.nextToken(); 165 if ( ( t != null ) && ( t.length() > 0 ) ) { 166 vec.add( t.trim() ); 167 } 168 } 169 } else { 170 vec.add( s ); 171 } 172 173 String[] kw = vec.toArray( new String[vec.size()] ); 174 if ( deleteDoubles ) { 175 kw = deleteDoubles( kw ); 176 } 177 178 return kw; 179 } 180 181 /** 182 * parse a string and return its tokens as typed List. empty fields will be removed from the list. 183 * 184 * @param s 185 * string to parse 186 * @param delimiter 187 * delimiter that marks the end of a token 188 * @param deleteDoubles 189 * if it's true all string that are already within the resulting array will be deleted, so that there 190 * will only be one copy of them. 191 * @return a list of Strings 192 */ 193 public static List<String> toList( String s, String delimiter, boolean deleteDoubles ) { 194 if ( s == null || s.equals( "" ) ) { 195 return new ArrayList<String>(); 196 } 197 198 StringTokenizer st = new StringTokenizer( s, delimiter ); 199 ArrayList<String> vec = new ArrayList<String>( st.countTokens() ); 200 for ( int i = 0; st.hasMoreTokens(); i++ ) { 201 String t = st.nextToken(); 202 if ( ( t != null ) && ( t.length() > 0 ) ) { 203 if ( deleteDoubles ) { 204 if ( !vec.contains( t.trim() ) ) { 205 vec.add( t.trim() ); 206 } 207 } else { 208 vec.add( t.trim() ); 209 } 210 } 211 } 212 213 return vec; 214 } 215 216 /** 217 * transforms a string array to one string. the array fields are separated by the submitted delimiter: 218 * 219 * @param s 220 * stringarray to transform 221 * @param delimiter 222 * @return the String representation of the given array 223 */ 224 public static String arrayToString( String[] s, char delimiter ) { 225 StringBuffer res = new StringBuffer( s.length * 20 ); 226 227 for ( int i = 0; i < s.length; i++ ) { 228 res.append( s[i] ); 229 230 if ( i < ( s.length - 1 ) ) { 231 res.append( delimiter ); 232 } 233 } 234 235 return res.toString(); 236 } 237 238 /** 239 * transforms a list to one string. the array fields are separated by the submitted delimiter: 240 * 241 * @param s 242 * stringarray to transform 243 * @param delimiter 244 * @return the String representation of the given list. 245 */ 246 public static String listToString( List<?> s, char delimiter ) { 247 StringBuffer res = new StringBuffer( s.size() * 20 ); 248 249 for ( int i = 0; i < s.size(); i++ ) { 250 res.append( s.get( i ) ); 251 252 if ( i < ( s.size() - 1 ) ) { 253 res.append( delimiter ); 254 } 255 } 256 257 return res.toString(); 258 } 259 260 /** 261 * transforms a double array to one string. the array fields are separated by the submitted delimiter: 262 * 263 * @param s 264 * string array to transform 265 * @param delimiter 266 * @return the String representation of the given array 267 */ 268 public static String arrayToString( double[] s, char delimiter ) { 269 StringBuffer res = new StringBuffer( s.length * 20 ); 270 271 for ( int i = 0; i < s.length; i++ ) { 272 res.append( Double.toString( s[i] ) ); 273 274 if ( i < ( s.length - 1 ) ) { 275 res.append( delimiter ); 276 } 277 } 278 279 return res.toString(); 280 } 281 282 /** 283 * transforms a float array to one string. the array fields are separated by the submitted delimiter: 284 * 285 * @param s 286 * float array to transform 287 * @param delimiter 288 * @return the String representation of the given array 289 */ 290 public static String arrayToString( float[] s, char delimiter ) { 291 StringBuffer res = new StringBuffer( s.length * 20 ); 292 293 for ( int i = 0; i < s.length; i++ ) { 294 res.append( Float.toString( s[i] ) ); 295 296 if ( i < ( s.length - 1 ) ) { 297 res.append( delimiter ); 298 } 299 } 300 301 return res.toString(); 302 } 303 304 /** 305 * transforms a int array to one string. the array fields are separated by the submitted delimiter: 306 * 307 * @param s 308 * stringarray to transform 309 * @param delimiter 310 * @return the String representation of the given array 311 */ 312 public static String arrayToString( int[] s, char delimiter ) { 313 StringBuffer res = new StringBuffer( s.length * 20 ); 314 315 for ( int i = 0; i < s.length; i++ ) { 316 res.append( Integer.toString( s[i] ) ); 317 318 if ( i < ( s.length - 1 ) ) { 319 res.append( delimiter ); 320 } 321 } 322 323 return res.toString(); 324 } 325 326 /** 327 * clears the begin and end of a string from the strings submitted 328 * 329 * @param s 330 * string to validate 331 * @param mark 332 * string to remove from begin and end of <code>s</code> 333 * @return the substring of the given String without the mark at the and and the begin, and trimmed 334 */ 335 public static String validateString( String s, String mark ) { 336 if ( s == null ) { 337 return null; 338 } 339 340 if ( s.length() == 0 ) { 341 return s; 342 } 343 344 s = s.trim(); 345 346 while ( s.startsWith( mark ) ) { 347 s = s.substring( mark.length(), s.length() ).trim(); 348 } 349 350 while ( s.endsWith( mark ) ) { 351 s = s.substring( 0, s.length() - mark.length() ).trim(); 352 } 353 354 return s; 355 } 356 357 /** 358 * deletes all double entries from the submitted array 359 * 360 * @param s 361 * to remove the doubles from 362 * @return The string array without all doubled values 363 */ 364 public static String[] deleteDoubles( String[] s ) { 365 ArrayList<String> vec = new ArrayList<String>( s.length ); 366 367 for ( int i = 0; i < s.length; i++ ) { 368 if ( !vec.contains( s[i] ) ) { 369 vec.add( s[i] ); 370 } 371 } 372 373 return vec.toArray( new String[vec.size()] ); 374 } 375 376 /** 377 * removes all fields from the array that equals <code>s</code> 378 * 379 * @param target 380 * array where to remove the submitted string 381 * @param s 382 * string to remove 383 * @return the String array with all exact occurrences of given String removed. 384 */ 385 public static String[] removeFromArray( String[] target, String s ) { 386 ArrayList<String> vec = new ArrayList<String>( target.length ); 387 388 for ( int i = 0; i < target.length; i++ ) { 389 if ( !target[i].equals( s ) ) { 390 vec.add( target[i] ); 391 } 392 } 393 394 return vec.toArray( new String[vec.size()] ); 395 } 396 397 /** 398 * checks if the submitted array contains the string <code>value</code> 399 * 400 * @param target 401 * array to check if it contains <code>value</code> 402 * @param value 403 * string to check if it within the array 404 * @return true if the given value is contained (without case comparison) in the array, caution, if the value ends 405 * with a comma ',' a substring will be taken to remove it (rb: For whatever reason??). 406 */ 407 public static boolean contains( String[] target, String value ) { 408 if ( target == null || value == null ) { 409 return false; 410 } 411 412 if ( value.endsWith( "," ) ) { 413 value = value.substring( 0, value.length() - 1 ); 414 } 415 416 for ( int i = 0; i < target.length; i++ ) { 417 if ( value.equalsIgnoreCase( target[i] ) ) { 418 return true; 419 } 420 } 421 422 return false; 423 } 424 425 /** 426 * convert the array of string like [(x1,y1),(x2,y2)...] into an array of double [x1,y1,x2,y2...] 427 * 428 * @param s 429 * @param delimiter 430 * 431 * @return the array representation of the given String 432 */ 433 public static double[] toArrayDouble( String s, String delimiter ) { 434 if ( s == null || "".equals( s.trim() ) ) { 435 return null; 436 } 437 StringTokenizer st = new StringTokenizer( s, delimiter ); 438 439 ArrayList<String> vec = new ArrayList<String>( st.countTokens() ); 440 441 for ( int i = 0; st.hasMoreTokens(); i++ ) { 442 String t = st.nextToken().replace( ' ', '+' ); 443 444 if ( ( t != null ) && ( t.length() > 0 ) ) { 445 vec.add( t.trim().replace( ',', '.' ) ); 446 } 447 } 448 449 double[] array = new double[vec.size()]; 450 451 for ( int i = 0; i < vec.size(); i++ ) { 452 array[i] = Double.parseDouble( vec.get( i ) ); 453 } 454 455 return array; 456 } 457 458 /** 459 * convert the array of string like [(x1,y1),(x2,y2)...] into an array of float values [x1,y1,x2,y2...] 460 * 461 * @param s 462 * @param delimiter 463 * 464 * @return the array representation of the given String 465 */ 466 public static float[] toArrayFloat( String s, String delimiter ) { 467 if ( s == null ) { 468 return null; 469 } 470 471 if ( s.equals( "" ) ) { 472 return null; 473 } 474 475 StringTokenizer st = new StringTokenizer( s, delimiter ); 476 477 ArrayList<String> vec = new ArrayList<String>( st.countTokens() ); 478 for ( int i = 0; st.hasMoreTokens(); i++ ) { 479 String t = st.nextToken().replace( ' ', '+' ); 480 if ( ( t != null ) && ( t.length() > 0 ) ) { 481 vec.add( t.trim().replace( ',', '.' ) ); 482 } 483 } 484 485 float[] array = new float[vec.size()]; 486 487 for ( int i = 0; i < vec.size(); i++ ) { 488 array[i] = Float.parseFloat( vec.get( i ) ); 489 } 490 491 return array; 492 } 493 494 /** 495 * convert the array of string like [(x1,y1),(x2,y2)...] into an array of float values [x1,y1,x2,y2...] 496 * 497 * @param s 498 * @param delimiter 499 * 500 * @return the array representation of the given String 501 */ 502 public static int[] toArrayInt( String s, String delimiter ) { 503 if ( s == null ) { 504 return null; 505 } 506 507 if ( s.equals( "" ) ) { 508 return null; 509 } 510 511 StringTokenizer st = new StringTokenizer( s, delimiter ); 512 513 ArrayList<String> vec = new ArrayList<String>( st.countTokens() ); 514 for ( int i = 0; st.hasMoreTokens(); i++ ) { 515 String t = st.nextToken(); 516 if ( ( t != null ) && ( t.length() > 0 ) ) { 517 vec.add( t.trim() ); 518 } 519 } 520 521 int[] array = new int[vec.size()]; 522 523 for ( int i = 0; i < vec.size(); i++ ) { 524 array[i] = Integer.parseInt( vec.get( i ) ); 525 } 526 527 return array; 528 } 529 530 /** 531 * prints current stactrace 532 */ 533 public static void printStacktrace() { 534 System.out.println( StringTools.stackTraceToString( Thread.getAllStackTraces().get( Thread.currentThread() ) ) ); 535 } 536 537 /** 538 * transforms an array of StackTraceElements into a String 539 * 540 * @param se 541 * to put to String 542 * @return a String representation of the given Stacktrace. 543 */ 544 public static String stackTraceToString( StackTraceElement[] se ) { 545 546 StringBuffer sb = new StringBuffer(); 547 for ( int i = 0; i < se.length; i++ ) { 548 sb.append( se[i].getClassName() + " " ); 549 sb.append( se[i].getFileName() + " " ); 550 sb.append( se[i].getMethodName() + "(" ); 551 sb.append( se[i].getLineNumber() + ")\n" ); 552 } 553 return sb.toString(); 554 } 555 556 /** 557 * Get the message and the class, as well as the stack trace of the passed Throwable and transforms it into a String 558 * 559 * @param e 560 * to get information from 561 * @return the String representation of the given Throwable 562 */ 563 public static String stackTraceToString( Throwable e ) { 564 if ( e == null ) { 565 return "No Throwable given."; 566 } 567 StringBuffer sb = new StringBuffer(); 568 sb.append( e.getMessage() ).append( "\n" ); 569 sb.append( e.getClass().getName() ).append( "\n" ); 570 sb.append( stackTraceToString( e.getStackTrace() ) ); 571 return sb.toString(); 572 } 573 574 /** 575 * countString count the occurrences of token into target 576 * 577 * @param target 578 * @param token 579 * 580 * @return the number of occurrences of the given token in the given String 581 */ 582 public static int countString( String target, String token ) { 583 int start = target.indexOf( token ); 584 int count = 0; 585 586 while ( start != -1 ) { 587 count++; 588 start = target.indexOf( token, start + 1 ); 589 } 590 591 return count; 592 } 593 594 /** 595 * Extract all the strings that begin with "start" and end with "end" and store it into an array of String 596 * 597 * @param target 598 * @param startString 599 * @param endString 600 * 601 * @return <code>null</code> if no strings were found!! 602 */ 603 public static String[] extractStrings( String target, String startString, String endString ) { 604 int start = target.indexOf( startString ); 605 606 if ( start == -1 ) { 607 return null; 608 } 609 610 int count = countString( target, startString ); 611 String[] subString = null; 612 if ( startString.equals( endString ) ) { 613 count = count / 2; 614 subString = new String[count]; 615 for ( int i = 0; i < count; i++ ) { 616 int tmp = target.indexOf( endString, start + 1 ); 617 subString[i] = target.substring( start, tmp + 1 ); 618 start = target.indexOf( startString, tmp + 1 ); 619 } 620 } else { 621 subString = new String[count]; 622 for ( int i = 0; i < count; i++ ) { 623 subString[i] = target.substring( start, target.indexOf( endString, start + 1 ) + 1 ); 624 subString[i] = extractString( subString[i], startString, endString, true, true ); 625 start = target.indexOf( startString, start + 1 ); 626 } 627 } 628 629 return subString; 630 } 631 632 /** 633 * extract a string contained between startDel and endDel, you can remove the delimiters if set true the parameters 634 * delStart and delEnd 635 * 636 * @param target 637 * to extract from 638 * @param startDel 639 * to remove from the start 640 * @param endDel 641 * string to remove from the end 642 * @param delStart 643 * true if the start should be removed 644 * @param delEnd 645 * true if the end should be removed 646 * 647 * @return the extracted string from the given target. rb: Caution this method may not do what it should. 648 */ 649 public static String extractString( String target, String startDel, String endDel, boolean delStart, boolean delEnd ) { 650 if ( target == null ) { 651 return null; 652 } 653 int start = target.indexOf( startDel ); 654 655 if ( start == -1 ) { 656 return null; 657 } 658 659 String s = target.substring( start, target.indexOf( endDel, start + 1 ) + 1 ); 660 661 s = s.trim(); 662 663 if ( delStart ) { 664 while ( s.startsWith( startDel ) ) { 665 s = s.substring( startDel.length(), s.length() ).trim(); 666 } 667 } 668 669 if ( delEnd ) { 670 while ( s.endsWith( endDel ) ) { 671 s = s.substring( 0, s.length() - endDel.length() ).trim(); 672 } 673 } 674 675 return s; 676 } 677 678 /** 679 * Initialize the substitution map with all normalization rules for a given locale and add this map to the static 680 * localeMap. 681 * 682 * @param locale 683 * @throws IOException 684 * @throws SAXException 685 * @throws XMLParsingException 686 */ 687 private static void initMap( String locale ) 688 throws IOException, SAXException, XMLParsingException { 689 690 // read normalization file 691 StringBuffer sb = new StringBuffer( 1000 ); 692 InputStream is = StringTools.class.getResourceAsStream( "/normalization.xml" ); 693 if ( is == null ) { 694 is = StringTools.class.getResourceAsStream( "normalization.xml" ); 695 } 696 BufferedReader br = new BufferedReader( new InputStreamReader( is ) ); 697 String s = null; 698 while ( ( s = br.readLine() ) != null ) { 699 sb.append( s ); 700 } 701 br.close(); 702 703 // transform into xml fragment 704 XMLFragment xml = new XMLFragment(); 705 xml.load( new StringReader( sb.toString() ), StringTools.class.getResource( "normalization.xml" ).toString() ); // FIXME 706 707 // create map 708 Map<String, String> substitutionMap = new HashMap<String, String>( 20 ); 709 710 // extract case attrib ( "toLower" or "toUpper" or missing ) for passed locale 711 String xpath = "Locale[@name = '" + Locale.GERMANY.getLanguage() + "']/@case"; 712 String letterCase = XMLTools.getNodeAsString( xml.getRootElement(), xpath, 713 CommonNamespaces.getNamespaceContext(), null ); 714 if ( letterCase != null ) { 715 substitutionMap.put( "case", letterCase ); 716 } 717 718 // extract removeDoubles attrib ( "true" or "false" ) for passed locale 719 xpath = "Locale[@name = '" + Locale.GERMANY.getLanguage() + "']/@removeDoubles"; 720 String removeDoubles = XMLTools.getNodeAsString( xml.getRootElement(), xpath, 721 CommonNamespaces.getNamespaceContext(), null ); 722 if ( removeDoubles != null && removeDoubles.length() > 0 ) { 723 substitutionMap.put( "removeDoubles", removeDoubles ); 724 } 725 726 // extract rules section for passed locale 727 xpath = "Locale[@name = '" + locale + "']/Rule"; 728 List<Node> list = XMLTools.getNodes( xml.getRootElement(), xpath, CommonNamespaces.getNamespaceContext() ); 729 if ( list != null ) { 730 // for ( int i = 0; i < list.size(); i++ ) { 731 for ( Node n : list ) { 732 String src = XMLTools.getRequiredNodeAsString( n, "Source", CommonNamespaces.getNamespaceContext() ); 733 String target = XMLTools.getRequiredNodeAsString( n, "Target", CommonNamespaces.getNamespaceContext() ); 734 substitutionMap.put( src, target ); 735 } 736 } 737 738 // init localeMap if needed 739 if ( localeMap == null ) { 740 localeMap = new HashMap<String, Map<String, String>>( 20 ); 741 } 742 743 localeMap.put( locale, substitutionMap ); 744 } 745 746 /** 747 * The passed string gets normalized along the rules for the given locale as they are set in the file 748 * "./normalization.xml". If such rules are specified, the following order is obeyed: 749 * 750 * <ol> 751 * <li>if the attribute "case" is set with "toLower" or "toUpper", the letters are switched to lower case or to 752 * upper case respectively.</li> 753 * <li>all rules given in the "Rule" elements are performed.</li> 754 * <li>if the attribute "removeDoubles" is set and not empty, all multi occurences of the letters given in this 755 * attribute are reduced to a single occurence.</li> 756 * </ol> 757 * 758 * @param source 759 * the String to normalize 760 * @param locale 761 * the locale language defining the rules to choose, e.g. "de" 762 * @return the normalized String 763 * @throws IOException 764 * @throws SAXException 765 * @throws XMLParsingException 766 */ 767 public static String normalizeString( String source, String locale ) 768 throws IOException, SAXException, XMLParsingException { 769 770 if ( localeMap == null ) { 771 localeMap = new HashMap<String, Map<String, String>>( 20 ); 772 } 773 Map<String, String> substitutionMap = localeMap.get( locale ); 774 775 if ( substitutionMap == null ) { 776 initMap( locale ); 777 } 778 substitutionMap = localeMap.get( locale ); 779 780 String output = source; 781 Set<String> keys = substitutionMap.keySet(); 782 783 boolean toUpper = false; 784 boolean toLower = false; 785 boolean removeDoubles = false; 786 787 for ( String key : keys ) { 788 if ( "case".equals( key ) ) { 789 toUpper = "toUpper".equals( substitutionMap.get( key ) ); 790 toLower = "toLower".equals( substitutionMap.get( key ) ); 791 } 792 if ( "removeDoubles".equals( key ) && substitutionMap.get( key ).length() > 0 ) { 793 removeDoubles = true; 794 } 795 } 796 797 // first: change letters to upper / lower case 798 if ( toUpper ) { 799 output = output.toUpperCase(); 800 } else if ( toLower ) { 801 output = output.toLowerCase(); 802 } 803 804 // second: change string according to specified rules 805 for ( String key : keys ) { 806 if ( !"case".equals( key ) && !"removeDoubles".equals( key ) ) { 807 output = output.replaceAll( key, substitutionMap.get( key ) ); 808 } 809 } 810 811 // third: remove doubles 812 if ( removeDoubles ) { 813 String doubles = substitutionMap.get( "removeDoubles" ); 814 for ( int i = 0; i < doubles.length(); i++ ) { 815 String remove = "" + doubles.charAt( i ) + "+"; 816 String replaceWith = "" + doubles.charAt( i ); 817 output = output.replaceAll( remove, replaceWith ); 818 } 819 } 820 return output; 821 } 822 823 /** 824 * prints a map with one line for each key-value pair 825 * @param map 826 * @param ps if ps is null System.out will be used 827 */ 828 public static final void printMap( Map<?, ?> map, PrintStream ps ) { 829 if ( ps == null ) { 830 ps = System.out; 831 } 832 Iterator<?> iter = map.keySet().iterator(); 833 while ( iter.hasNext() ) { 834 Object key = (Object) iter.next(); 835 Object value = map.get( key ); 836 ps.println( key + " : " + value ); 837 } 838 } 839 }