/*---------------- FILE HEADER ------------------------------------------
This file is part of deegree.
Copyright (C) 2001-2006 by:
EXSE, Department of Geography, University of Bonn
http://www.giub.uni-bonn.de/deegree/
lat/lon GmbH
http://www.lat-lon.de
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Contact:
Andreas Poth
lat/lon GmbH
Aennchenstr. 19
53115 Bonn
Germany
E-Mail: poth@lat-lon.de
Prof. Dr. Klaus Greve
Department of Geography
University of Bonn
Meckenheimer Allee 166
53115 Bonn
Germany
E-Mail: greve@giub.uni-bonn.de
---------------------------------------------------------------------------*/
package org.deegree.framework.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import org.deegree.framework.xml.XMLFragment;
import org.deegree.framework.xml.XMLParsingException;
import org.deegree.framework.xml.XMLTools;
import org.deegree.ogcbase.CommonNamespaces;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
/**
* this is a collection of some methods that extends the functionallity of the sun-java string
* class.
*
* @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
* @author last edited by: $Author: mays $
*
* @version $Revision: 1.21 $, $Date: 2006/11/13 11:02:29 $
*/
public class StringTools {
/**
* This map is used for methods normalizeString() and initMap().
*
* key = locale language, e.g. "de"
* value = map of substitution rules for this locale
*/
private static Map<String, Map<String, String>> localeMap;
/**
* concatenates an array of strings using a
*
* @see StringBuffer
*
* @param size
* estimated size of the target string
* @param objects
* toString() will be called for each object to append it to the result string
* @return
*/
public static String concat( int size, Object... objects ) {
StringBuilder sbb = new StringBuilder( size );
for ( int i = 0; i < objects.length; i++ ) {
sbb.append( objects[i] );
}
return sbb.toString();
}
/**
* replaces occurences of a string fragment within a string by a new string.
*
* @param target
* is the original string
* @param from
* is the string to be replaced
* @param to
* is the string which will used to replace
* @param all
* if it's true all occurences of the string to be replaced will be replaced. else
* only the first occurence will be replaced.
*/
public static String replace( String target, String from, String to, boolean all ) {
StringBuffer buffer = new StringBuffer( target.length() );
int copyFrom = 0;
char[] targetChars = null;
int lf = from.length();
int start = -1;
do {
start = target.indexOf( from );
copyFrom = 0;
if ( start == -1 ) {
return target;
}
targetChars = target.toCharArray();
while ( start != -1 ) {
buffer.append( targetChars, copyFrom, start - copyFrom );
buffer.append( to );
copyFrom = start + lf;
start = target.indexOf( from, copyFrom );
if ( !all ) {
start = -1;
}
}
buffer.append( targetChars, copyFrom, targetChars.length - copyFrom );
target = buffer.toString();
buffer.delete( 0, buffer.length() );
} while ( target.indexOf( from ) > -1 && to.indexOf( from ) < 0 );
return target;
}
/**
* replaces all special german letters (umlaute)
*
* draft version
*/
public static String HTMLEncode( String source ) {
String target = null;
target = StringTools.replace( source, "�", "ä", true );
target = StringTools.replace( target, "�", "Ä", true );
target = StringTools.replace( target, "�", "ö", true );
target = StringTools.replace( target, "�", "Ö", true );
target = StringTools.replace( target, "�", "ü", true );
target = StringTools.replace( target, "�", "Ü", true );
target = StringTools.replace( target, "�", "ß", true );
return target;
}
/**
* parse a string and return its tokens as array
*
* @param s
* string to parse
* @param delimiter
* delimiter that marks the end of a token
* @param deleteDoubles
* if it's true all string that are already within the resulting array will be
* deleted, so that there will only be one copy of them.
*/
public static String[] toArray( String s, String delimiter, boolean deleteDoubles ) {
if ( s == null || s.equals( "" ) ) {
return new String[0];
}
StringTokenizer st = new StringTokenizer( s, delimiter );
ArrayList vec = new ArrayList( st.countTokens() );
for ( int i = 0; st.hasMoreTokens(); i++ ) {
String t = st.nextToken();
if ( ( t != null ) && ( t.length() > 0 ) ) {
vec.add( t.trim() );
}
}
// no value selected
if ( vec.size() == 0 ) {
return new String[0];
}
String[] kw = (String[]) vec.toArray( new String[vec.size()] );
if ( deleteDoubles ) {
kw = deleteDoubles( kw );
}
return kw;
}
/**
* parse a string and return its tokens as typed List
*
* @param s
* string to parse
* @param delimiter
* delimiter that marks the end of a token
* @param deleteDoubles
* if it's true all string that are already within the resulting array will be
* deleted, so that there will only be one copy of them.
* @return
*/
public static List<String> toList( String s, String delimiter, boolean deleteDoubles ) {
if ( s == null || s.equals( "" ) ) {
return new ArrayList<String>();
}
StringTokenizer st = new StringTokenizer( s, delimiter );
ArrayList<String> vec = new ArrayList<String>( st.countTokens() );
for ( int i = 0; st.hasMoreTokens(); i++ ) {
String t = st.nextToken();
if ( ( t != null ) && ( t.length() > 0 ) ) {
if ( deleteDoubles ) {
if ( !vec.contains( t.trim() ) ) {
vec.add( t.trim() );
}
} else {
vec.add( t.trim() );
}
}
}
return vec;
}
/**
* transforms a string array to one string. the array fields are seperated by the submitted
* delimiter:
*
* @param s
* stringarray to transform
* @param delimiter
*/
public static String arrayToString( String[] s, char delimiter ) {
StringBuffer res = new StringBuffer( s.length * 20 );
for ( int i = 0; i < s.length; i++ ) {
res.append( s[i] );
if ( i < ( s.length - 1 ) ) {
res.append( delimiter );
}
}
return res.toString();
}
/**
* transforms a list to one string. the array fields are seperated by the submitted delimiter:
*
* @param s
* stringarray to transform
* @param delimiter
*/
public static String listToString( List s, char delimiter ) {
StringBuffer res = new StringBuffer( s.size() * 20 );
for ( int i = 0; i < s.size(); i++ ) {
res.append( s.get( i ) );
if ( i < ( s.size() - 1 ) ) {
res.append( delimiter );
}
}
return res.toString();
}
/**
* transforms a double array to one string. the array fields are seperated by the submitted
* delimiter:
*
* @param s
* stringarray to transform
* @param delimiter
*/
public static String arrayToString( double[] s, char delimiter ) {
StringBuffer res = new StringBuffer( s.length * 20 );
for ( int i = 0; i < s.length; i++ ) {
res.append( Double.toString( s[i] ) );
if ( i < ( s.length - 1 ) ) {
res.append( delimiter );
}
}
return res.toString();
}
/**
* transforms a int array to one string. the array fields are seperated by the submitted
* delimiter:
*
* @param s
* stringarray to transform
* @param delimiter
*/
public static String arrayToString( int[] s, char delimiter ) {
StringBuffer res = new StringBuffer( s.length * 20 );
for ( int i = 0; i < s.length; i++ ) {
res.append( Integer.toString( s[i] ) );
if ( i < ( s.length - 1 ) ) {
res.append( delimiter );
}
}
return res.toString();
}
/**
* clears the begin and end of a string from the strings sumitted
*
* @param s
* string to validate
* @param mark
* string to remove from begin and end of <code>s</code>
*/
public static String validateString( String s, String mark ) {
if ( s == null ) {
return null;
}
if ( s.length() == 0 ) {
return s;
}
s = s.trim();
while ( s.startsWith( mark ) ) {
s = s.substring( mark.length(), s.length() ).trim();
}
while ( s.endsWith( mark ) ) {
s = s.substring( 0, s.length() - mark.length() ).trim();
}
return s;
}
/**
* deletes all double entries from the submitted array
*/
public static String[] deleteDoubles( String[] s ) {
ArrayList vec = new ArrayList( s.length );
for ( int i = 0; i < s.length; i++ ) {
if ( !vec.contains( s[i] ) ) {
vec.add( s[i] );
}
}
return (String[]) vec.toArray( new String[vec.size()] );
}
/**
* removes all fields from the array that equals <code>s</code>
*
* @param target
* array where to remove the submitted string
* @param s
* string to remove
*/
public static String[] removeFromArray( String[] target, String s ) {
ArrayList vec = new ArrayList( target.length );
for ( int i = 0; i < target.length; i++ ) {
if ( !target[i].equals( s ) ) {
vec.add( target[i] );
}
}
return (String[]) vec.toArray( new String[vec.size()] );
}
/**
* checks if the submitted array contains the string <code>value</code>
*
* @param target
* array to check if it contains <code>value</code>
* @param value
* string to check if it within the array
*/
public static boolean contains( String[] target, String value ) {
if ( target == null || value == null ) {
return false;
}
if ( value.endsWith( "," ) ) {
value = value.substring( 0, value.length() - 1 );
}
for ( int i = 0; i < target.length; i++ ) {
if ( value.equalsIgnoreCase( target[i] ) ) {
return true;
}
}
return false;
}
/**
* convert the array of string like [(x1,y1),(x2,y2)...] into an array of double
* [x1,y1,x2,y2...]
*
* @param s
* @param delimiter
*
* @return
*/
public static double[] toArrayDouble( String s, String delimiter ) {
if ( s == null ) {
return null;
}
if ( s.equals( "" ) ) {
return null;
}
StringTokenizer st = new StringTokenizer( s, delimiter );
ArrayList vec = new ArrayList( st.countTokens() );
for ( int i = 0; st.hasMoreTokens(); i++ ) {
String t = st.nextToken().replace( ' ', '+' );
if ( ( t != null ) && ( t.length() > 0 ) ) {
vec.add( t.trim() );
}
}
double[] array = new double[vec.size()];
for ( int i = 0; i < vec.size(); i++ ) {
array[i] = Double.parseDouble( (String) vec.get( i ) );
}
return array;
}
/**
* convert the array of string like [(x1,y1),(x2,y2)...] into an array of
* float values [x1,y1,x2,y2...]
*
* @param s
* @param delimiter
*
* @return
*/
public static float[] toArrayFloat( String s, String delimiter ) {
if ( s == null ) {
return null;
}
if ( s.equals( "" ) ) {
return null;
}
StringTokenizer st = new StringTokenizer( s, delimiter );
ArrayList vec = new ArrayList( st.countTokens() );
for ( int i = 0; st.hasMoreTokens(); i++ ) {
String t = st.nextToken().replace( ' ', '+' );
if ( ( t != null ) && ( t.length() > 0 ) ) {
vec.add( t.trim() );
}
}
float[] array = new float[vec.size()];
for ( int i = 0; i < vec.size(); i++ ) {
array[i] = Float.parseFloat( (String) vec.get( i ) );
}
return array;
}
/**
* transforms an array of StackTraceElements into a String
*/
public static String stackTraceToString( StackTraceElement[] se ) {
StringBuffer sb = new StringBuffer();
for ( int i = 0; i < se.length; i++ ) {
sb.append( se[i].getClassName() + " " );
sb.append( se[i].getFileName() + " " );
sb.append( se[i].getMethodName() + "(" );
sb.append( se[i].getLineNumber() + ")\n" );
}
return sb.toString();
}
/**
* gets the stacktrace array from the passed Excption and transforms it into a String
*/
public static String stackTraceToString( Throwable e ) {
StackTraceElement[] se = e.getStackTrace();
StringBuffer sb = new StringBuffer();
sb.append( e.getMessage() ).append( "\n" );
sb.append( e.getClass().getName() ).append( "\n" );
for ( int i = 0; i < se.length; i++ ) {
sb.append( se[i].getClassName() + " " );
sb.append( se[i].getFileName() + " " );
sb.append( se[i].getMethodName() + "(" );
sb.append( se[i].getLineNumber() + ")\n" );
if ( i > 4 )
break;
}
return sb.toString();
}
/**
* countString count the occurrences of token into target
*
* @param target
* @param token
*
* @return
*/
public static int countString( String target, String token ) {
int start = target.indexOf( token );
int count = 0;
while ( start != -1 ) {
count++;
start = target.indexOf( token, start + 1 );
}
return count;
}
/**
* Extract all the strings that begin with "start" and end with "end" and store it into an array
* of String
*
* @param target
* @param startString
* @param endString
*
* @return
*/
public static String[] extractStrings( String target, String startString, String endString ) {
int start = target.indexOf( startString );
if ( start == -1 ) {
return null;
}
int count = countString( target, startString );
String[] subString = null;
if ( startString.equals( endString ) ) {
count = count / 2;
subString = new String[count];
for ( int i = 0; i < count; i++ ) {
int tmp = target.indexOf( endString, start + 1 );
subString[i] = target.substring( start, tmp + 1 );
start = target.indexOf( startString, tmp + 1 );
}
} else {
subString = new String[count];
for ( int i = 0; i < count; i++ ) {
subString[i] = target.substring( start, target.indexOf( endString, start + 1 ) + 1 );
subString[i] = extractString( subString[i], startString, endString, true, true );
start = target.indexOf( startString, start + 1 );
}
}
return subString;
}
/**
* extract a string contained between startDel and endDel, you can remove the delimiters if set
* true the parameters delStart and delEnd
*
* @param target
* @param startDel
* @param endDel
* @param delStart
* @param delEnd
*
* @return
*/
public static String extractString( String target, String startDel, String endDel,
boolean delStart, boolean delEnd ) {
int start = target.indexOf( startDel );
if ( start == -1 ) {
return null;
}
String s = target.substring( start, target.indexOf( endDel, start + 1 ) + 1 );
s = s.trim();
if ( delStart ) {
while ( s.startsWith( startDel ) ) {
s = s.substring( startDel.length(), s.length() ).trim();
}
}
if ( delEnd ) {
while ( s.endsWith( endDel ) ) {
s = s.substring( 0, s.length() - endDel.length() ).trim();
}
}
return s;
}
/**
* Initialize the substitution map with all normalization rules for a given locale and
* add this map to the static localeMap.
*
* @param locale
* @throws IOException
* @throws SAXException
* @throws XMLParsingException
*/
private static void initMap( String locale )
throws IOException, SAXException, XMLParsingException {
// read normalization file
StringBuffer sb = new StringBuffer( 1000 );
InputStream is = StringTools.class.getResourceAsStream( "normalization.xml" );
BufferedReader br = new BufferedReader( new InputStreamReader( is ) );
String s = null;
while ( ( s = br.readLine() ) != null ) {
sb.append( s );
}
br.close();
// transform into xml fragment
XMLFragment xml = new XMLFragment();
xml.load( new StringReader( sb.toString() ),
StringTools.class.getResource( "normalization.xml" ).toString() ); //FIXME
// create map
Map<String, String> substitutionMap = new HashMap<String, String>( 20 );
// extract case attrib ( "toLower" or "toUpper" or missing ) for passed locale
String xpath = "Locale[@name = '" + Locale.GERMANY.getLanguage() + "']/@case";
String letterCase = XMLTools.getNodeAsString( xml.getRootElement(), xpath,
CommonNamespaces.getNamespaceContext(), null );
if ( letterCase != null ) {
substitutionMap.put( "case", letterCase );
}
// extract removeDoubles attrib ( "true" or "false" ) for passed locale
xpath = "Locale[@name = '" + Locale.GERMANY.getLanguage() + "']/@removeDoubles";
String removeDoubles = XMLTools.getNodeAsString( xml.getRootElement(), xpath,
CommonNamespaces.getNamespaceContext(),
null );
if ( removeDoubles != null && removeDoubles.length() > 0 ) {
substitutionMap.put( "removeDoubles", removeDoubles );
}
// extract rules section for passed locale
xpath = "Locale[@name = '" + locale + "']/Rule";
List list = XMLTools.getNodes( xml.getRootElement(), xpath,
CommonNamespaces.getNamespaceContext() );
if ( list != null ) {
for ( int i = 0; i < list.size(); i++ ) {
String src = XMLTools.getRequiredNodeAsString( (Node) list.get( i ),
"Source",
CommonNamespaces.getNamespaceContext() );
String target = XMLTools.getRequiredNodeAsString( (Node) list.get( i ),
"Target",
CommonNamespaces.getNamespaceContext() );
substitutionMap.put( src, target );
}
}
// init localeMap if needed
if ( localeMap == null ) {
localeMap = new HashMap<String, Map<String, String>>( 20 );
}
localeMap.put( locale, substitutionMap );
}
/**
* The passed string gets normalized along the rules for the given locale as they are set in
* the file "./normalization.xml".
* If such rules are specified, the following order is obeyed:
*
* <ol>
* <li>if the attribute "case" is set with "toLower" or "toUpper", the letters are switched
* to lower case or to upper case respectively.</li>
* <li>all rules given in the "Rule" elements are performed.</li>
* <li>if the attribute "removeDoubles" is set and not empty, all multi occurences of the
* letters given in this attribute are reduced to a single occurence.</li>
* </ol>
*
* @param source the String to normalize
* @param locale the locale language defining the rules to choose, e.g. "de"
* @return the normalized String
* @throws IOException
* @throws SAXException
* @throws XMLParsingException
*/
public static String normalizeString( String source, String locale )
throws IOException, SAXException, XMLParsingException {
if ( localeMap == null ) {
localeMap = new HashMap<String, Map<String, String>>( 20 );
}
Map<String, String> substitutionMap = localeMap.get( locale );
if ( substitutionMap == null ) {
initMap( locale );
}
substitutionMap = localeMap.get( locale );
String output = source;
Set<String> keys = substitutionMap.keySet();
boolean toUpper = false;
boolean toLower = false;
boolean removeDoubles = false;
for ( String key : keys ) {
if ( "case".equals( key ) ) {
toUpper = "toUpper".equals( substitutionMap.get( key ) );
toLower = "toLower".equals( substitutionMap.get( key ) );
}
if ( "removeDoubles".equals( key ) && substitutionMap.get( key ).length() > 0 ) {
removeDoubles = true;
}
}
// first: change letters to upper / lower case
if ( toUpper ) {
output = output.toUpperCase();
} else if ( toLower ) {
output = output.toLowerCase();
}
// second: change string according to specified rules
for ( String key : keys ) {
if ( !"case".equals( key ) && !"removeDoubles".equals( key ) ) {
output = output.replaceAll( key, substitutionMap.get( key ) );
}
}
// third: remove doubles
if ( removeDoubles ) {
String doubles = substitutionMap.get( "removeDoubles" );
for ( int i = 0; i < doubles.length(); i++ ) {
String remove = "" + doubles.charAt( i ) + "+";
String replaceWith = "" + doubles.charAt( i );
output = output.replaceAll( remove, replaceWith );
}
}
return output;
}
}
/* ********************************************************************
Changes to this class. What the people have been up to:
$Log: StringTools.java,v $
Revision 1.21 2006/11/13 11:02:29 mays
add normalizeString() initMap() and localeMap
Revision 1.20 2006/09/11 11:25:40 poth
mathod toArrayFloat added
Revision 1.19 2006/09/07 12:50:19 poth
bug fix in method replace
Revision 1.18 2006/09/06 10:38:31 poth
bug fix
Revision 1.17 2006/09/06 08:20:42 poth
bug fix in method replace
Revision 1.16 2006/08/29 19:54:14 poth
footer corrected
Revision 1.15 2006/07/12 14:46:17 poth
comment footer added
********************************************************************** */