/* * Copyright (c) 2014. by Robusta Code and individual contributors * as indicated by the @authors tag. See the copyright.txt in the * distribution for a full listing of individual contributors. * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package io.robusta.rra.utils; import io.robusta.rra.exception.ValidationException; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * This class gives a few function to waork on Strings. * <p> * Most importants are probably * <ul> * <li>{@link StringUtils#validateEmail(String) validateEmail()} * <li>{@link StringUtils#replaceAny(String, String, String) replaceAny()} * <li> {@link StringUtils#removeCharacters(String, String) removeCharacters()} * </ul> * </p> * Created by Nicolas Zozol for Robusta Code * * @author Nicolas Zozol TODO : verify this class is GWT compatible TODO : check * if any is NOT used inside the library */ public class StringUtils { /** * This function test if an email is valid. It's not the best validation of * the world : all good emails will pass, but some wrong emails (even if * it's very unlikely) may also pass. * <p> * This function is based on the Javascript pattern * /^([a-zA-Z0-9_.-])+@([a-zA-Z0-9_.-])+\.([a-zA-Z])+([a-zA-Z])+/ * </p> * <p> * Check <a href="http://www.ietf.org/rfc/rfc2822.txt">IETF</a> for more * informations. * </p> * * @param email * email to be tested * @return true if email is an email pattern, false if not * @throws IllegalArgumentException * if the email is null */ public static boolean validateEmail( String email ) { if ( email == null ) { throw new IllegalArgumentException( "The email is null" ); } if ( email.equals( "" ) ) { return false; } String worker = new String( email ); worker = worker.trim(); return worker.matches( "^([a-zA-Z0-9_.-])+@([a-zA-Z0-9_.-])+\\.([a-zA-Z])+([a-zA-Z])+" ); /* * //Set the email pattern string //Pattern p = * Pattern.compile(".+@.+\\.[a-z]+"); Pattern p = Pattern.compile( * "^([a-zA-Z0-9_.-])+@([a-zA-Z0-9_.-])+\\.([a-zA-Z])+([a-zA-Z])+"); * //Match the given string with the pattern Matcher m = * p.matcher(worker); * * return m.matches(); */ } /** * Returns true if the String could be a md5 * * @param md5String * String to test : could it be a MD5, or there is no way ? * @return true if the String could be a md5 * @throws IllegalArgumentException * if the md5String is null */ public static boolean validateMD5( String md5String ) { if ( md5String == null ) { throw new IllegalArgumentException( "md5String is null" ); } return md5String.length() == 32 && md5String.matches( "([a-z0-9])+" ); } /** * Returns what is before the '@' character. For * <strong>nicolas.zozol@robustaweb.com</strong> email, we'll get here * <strong>nicolas.zozol</strong> * * @param email * @return The email prefix * @throws IllegalArgumentException * if the email is null * @throws ValidationException * if email is not a valid email */ public static String getEmailPrefix( String email ) throws ValidationException { if ( email == null ) { throw new IllegalArgumentException( "email is null" ); } if ( !validateEmail( email ) ) { throw new ValidationException( "Email is not valid" ); } int index = email.indexOf( '@' ); return email.substring( 0, index ); } /** * * Return true if the email is in two parts, like nicolas.zozol@gmail.com * Note that Bart.@simpsons.com will return true. * * @param email * @return true if the email is in two parts, like nicolas.zozol@gmail.com * @throws ValidationException * if the email is not valid */ public static boolean isEmailSplitInTwoParts( String email ) throws ValidationException { if ( !validateEmail( email ) ) { throw new ValidationException( "Email is not valid" ); } int index = email.indexOf( '@' ); String prefix = email.substring( 0, index ); return ( prefix.contains( "." ) ); } /** * <p> * removes a character in the String * </p> * * <p> * String boss="Bruce Springsteen";<br/> * String result = StringUtils.removeCharacter(boss, 'e') returns * "Bruc Springstn".<br/> * But the original String boss is not modified. * </p> * * @param string * @param c * @return */ public static String removeCharacter( String string, char c ) { String work = new String( string ); String s = "" + c; work = work.replace( s, "" ); return work; } /** * <p> * Removes in the string every characters inside charactersToRemove.<br/> * For exemple removeCharacters("Napoleon","nou") returns Naple because it * will remove one 'n', two 'o' and zero 'u' <br/> * Like for removeCharacter(String string, char c), the original String is * not modified. * </p> * <p> * It's a recursive function, not the fastest you may have. * </p> * * @param original * Original string * @param charactersToRemove * characters that will be removed form the Original String * @return a new string without the specified characters * @throws IllegalArgumentException * if original or charactersToRemove is null */ public static String removeCharacters( String original, String charactersToRemove ) { if ( original == null ) { throw new IllegalArgumentException( "Your original parameter is null" ); } if ( charactersToRemove == null ) { throw new IllegalArgumentException( "charactersToRemove parameter si null" ); } String work = new String( original ); char c; for ( int i = 0; i < charactersToRemove.length(); i++ ) { c = charactersToRemove.charAt( i ); work = removeCharacter( work, c ); } return work; } /** * Will remove all characters from the end. <br/> * <p> * removeStartingCharacters(iiiiMalagi, 'i') returns Malagi * </p> * If the string is empty, it returns also an empty string. If the string is * null, it will throw a IllegalArgumentException * * @param originalString * Original parameter * @param character * @throws IllegalArgumentException * : if the string is null * @return Removes all the specified character found at the beginning of the * string */ public static String removeStartingCharacters( String originalString, char character ) { if ( originalString == null ) { throw new IllegalArgumentException( "original String is null" ); } if ( originalString.length() == 0 ) { return originalString; } String result = new String( originalString ); while ( result.indexOf( character ) == 0 ) { result = removeFirstCharacter( result ); if ( result.length() == 0 ) { return ""; } } return result; } /** * Will remove all characters from the end. <br/> * <p> * removeTrailingCharacters(Malagaaaa, 'a') returns Malag * </p> * If the string is empty, it returns also an empty string. If the string is * null, it will throw a IllegalArgumentException * * @param originalString * Original parameter * @param character * @throws IllegalArgumentException * : if the string is null * @return Removes all the specified character found at the end of the * string */ public static String removeTrailingCharacters( String originalString, char character ) { if ( originalString == null ) { throw new IllegalArgumentException( "original String is null" ); } if ( originalString.length() == 0 ) { return originalString; } String result = new String( originalString ); while ( result.lastIndexOf( character ) == result.length() - 1 ) { result = removeLastCharacter( result ); if ( result.length() == 0 ) { return ""; } } return result; } /** * <p> * Simply removes the last character * </p> * * @param originalString * @return a new String without the * @throws IllegalArgumentException * if the string is null */ public static String removeFirstCharacter( String originalString ) { if ( originalString == null ) { throw new IllegalArgumentException( "string is null" ); } if ( originalString.length() <= 1 ) { return ""; } return originalString.substring( 1 ); } /** * <p> * Simply removes the last character * </p> * * @param originalString * @return a new String without the * @throws IllegalArgumentException * if the string is null */ public static String removeLastCharacter( String originalString ) { if ( originalString == null ) { throw new IllegalArgumentException( "string is null" ); } if ( originalString.length() <= 1 ) { return ""; } return originalString.substring( 0, originalString.length() - 1 ); } /** * <p> * This is a simpler alternative to original String.replaceAll function that * uses complex Regexp. Here, you modify a String by changing the * <strong>characters sequence</strong> sequenceToReplace by the replacement * string. * </p> * * <p> * Exemple :<br/> *  <code>String original = "//cooluri//living.js///";</code><br/> * Then <br/> *  <code>StringUtils.replaceAll(original,"//", "-");</code><br/> * will return :<br/> * -cooluri-living.js-/ * </p> * <p> * Note that the '///' is always seen as ('//'+'/') and * <strong>never</strong> as ('/'+'//'). This function is * <strong>sequential</strong>, and will not always work like regular * expressions. * </p> * * @param originalString * The original string * @param sequenceToReplace * The characters sequence to replace * @param replacement * @return * @throws IllegalArgumentException * if a param is null */ public static String replaceSequence( String originalString, String sequenceToReplace, String replacement ) { if ( originalString == null ) { throw new IllegalArgumentException( "originalString is null" ); } if ( sequenceToReplace == null ) { throw new IllegalArgumentException( "charsToReplace is null" ); } if ( replacement == null ) { throw new IllegalArgumentException( "replacement is null" ); } if ( sequenceToReplace.equals( replacement ) ) { return originalString; } String newString = new String( originalString ); if ( originalString == null || originalString.length() == 0 ) { return originalString; } if ( sequenceToReplace == null || sequenceToReplace.length() == 0 ) { return originalString; } int indexPiece = originalString.indexOf( sequenceToReplace ); while ( indexPiece != -1 ) { newString = newString.substring( 0, indexPiece ) + replacement + newString.substring( indexPiece + sequenceToReplace.length() ); indexPiece = newString.indexOf( sequenceToReplace ); } return newString; } /** * Replace any character defined in anyCharacter by a replacement sequence * Ex : * * <pre> * replaceAny("john jack jim", "ji", "t") => "tohn tack ttm" * </pre> * * @param originalString * @param anyCharacter * @param replacement * @return */ public static String replaceAny( String originalString, String anyCharacter, String replacement ) { String worker = originalString; for ( Character c : anyCharacter.toCharArray() ) { String pattern = "" + c; worker = worker.replaceAll( pattern, replacement ); } return worker; } /** * <p> * Keeps the 'number' first characters, and might be useful to keep your * user's privacy :<br/> *  <code>truncate("Hernandez", 3)</code><br/> * will produce: "Her" * </p> * <p> * If number is bigger than the size of the String, the whole string is kept * : <br/> *  <code>truncate("Hernandez", 24)</code><br/> * will produce: "Hernandez" * </p> * <p> * If number==0, the function returns an empty string. * </p> * * @param original * @param number * @return a new smaller String. * @throws IllegalArgumentException * if number<0 or original is null */ public static String truncate( String original, int number ) { if ( number < 0 ) { throw new IllegalArgumentException( "number :" + number + " is <0" ); } if ( original == null ) { throw new IllegalArgumentException( "original string is null" ); } String newLastName = original.length() >= number ? original.substring( 0, number ) : original; return newLastName; } /** * If stringToTes contains any character in 'characatersToTest', the * function returns true, false if not * * @param stringToTest * @param charatersToTest * @return true if stringToTes contains any character in 'characatersToTest' * @throws IllegalArgumentException * if charactersToTest is null or empty */ public static boolean containsCharacter( String stringToTest, String charatersToTest ) { if ( charatersToTest == null ) { throw new IllegalArgumentException( "characters is null" ); } if ( charatersToTest.length() == 0 ) { throw new IllegalArgumentException( "No character to Test" ); } for ( int i = 0; i < charatersToTest.length(); i++ ) { char c = charatersToTest.charAt( i ); if ( stringToTest.contains( String.valueOf( c ) ) ) { return true; } } return false; } /** * Returns the concatenation of the absolute and relative paths, adding a * "/" character between them if needed Calling addPath(uri, "/") ensures * that the path will finish with only one "/" (as long as uri doesn't * finishes with more than one "/") * * @param absolute * @param relative * @return */ public static String addPath( String absolute, String relative ) { return removeTrailingCharacters( absolute, '/' ) + "/" + removeStartingCharacters( relative, '/' ); } /** * Add the protocol to the String * * @param protocol * @param uri * @return a new Stirng with the protocol */ public static String addProtocol( String protocol, String uri ) { String worker = removeTrailingCharacters( protocol, '/' ); worker = removeTrailingCharacters( worker, ':' ); return worker + "://" + removeStartingCharacters( uri, '/' ); } /** * Needed for Gwt Compatibility : GWT does not yet provide a * getSimpleClassName * * @param o * @return */ public static String getSimpleClassName( Object o ) { String className = o.getClass().getName(); return className.substring( className.lastIndexOf( "." ) + 1, className.length() ); } /** * Transform an Obejct array into a String array, by calling toString() on * each element. * * @param array * @param transformNullIntoBlank * if true, a null is changed to blank. If false, we keep null * @return a String array */ public String[] stringify( Object[] array, boolean transformNullIntoBlank ) { ArrayList<String> result = new ArrayList<String>(); for ( Object o : array ) { if ( o != null ) { result.add( o.toString() ); } else { if ( transformNullIntoBlank ) { result.add( "" ); } else { result.add( null ); } } } return (String[]) result.toArray(); } /** * Return a String that starts with an Uppercase character, and ends with * LowerCase characters strictCapitalize("johnDoe") returns Johndoe * * @param str * string to capitalize * @return a String that starts with an Uppercase character, and ends with * LowerCase characters * @see #simpleCapitalize(java.lang.String) */ public static String strictCapitalize( String str ) { if ( str == null ) { throw new IllegalArgumentException( "String is null" ); } if ( str.length() == 0 ) { return str; } String worker = str.toLowerCase(); return worker.substring( 0, 1 ).toUpperCase() + worker.substring( 1 ); } /** * Capitalize the first letter, and doesn't touch any other * simpleCapitalize("johnDoe") returns JohnDoe * * @param str * string to capitalize * @return the same string with capitalized first letter * @see #strictCapitalize(java.lang.String) */ public static String simpleCapitalize( String str ) { if ( str == null ) { throw new IllegalArgumentException( "String is null" ); } if ( str.length() == 0 ) { return str; } String work = new String( str ); return work.substring( 0, 1 ).toUpperCase() + work.substring( 1 ); } /** * TODO 1 : work on this ; Use StringTokenizer * <p> * Splits the provided text into an array ,* separators specified. * </p> * * <p> * The separator is not included in the returned String array. Adjacent * separators are treated as one separator. * </p> * * <pre> * StringUtils.split("ab de fg", " ") = ["ab", "de", "fg"] * StringUtils.split("ab de fg", " ") = ["ab", "cd", "ef"] * StringUtils.split("ab:cd:ef", ":") = ["ab", "cd", "ef"] * </pre> * * @param string * the String to parse, may be null * @param separatorChars * the characters used as the delimiters * @return an array of parsed Strings * @throws IllegalArgumentException * if string or separatorChars is null, or if separatorChars is * empty */ public static String[] split( String string, String separatorChars ) { if ( string == null ) { throw new IllegalArgumentException( "string argument is null" ); } if ( separatorChars == null || separatorChars.length() == 0 ) { throw new IllegalArgumentException( "separators is null or empty" ); } List<String> resultList = new ArrayList<String>(); String worker = new String( string ); String firstSeparator = separatorChars.substring( 0, 1 ); assert firstSeparator.length() == 1; char[] charArray = separatorChars.toCharArray(); // We replace multiple instance of other separator by only one // firstSeparator for ( int i = 1; i < charArray.length; i++ ) { String pattern = "(" + charArray[i] + ")+"; worker = worker.replaceAll( pattern, firstSeparator ); } // One more pass for the first separator String pattern = "(" + firstSeparator + ")+"; worker = worker.replaceAll( pattern, firstSeparator ); System.out.println( "separator : " + firstSeparator + " ; worker : " + worker ); Collections.addAll( resultList, worker.split( firstSeparator ) ); return (String[]) resultList.toArray( new String[resultList.size()] ); } /** * @todo3 : to be tested Join elements * @param strings * @param glue * @return */ public static String join( Object[] strings, String glue ) { if ( strings == null ) { throw new IllegalArgumentException( "strings array is null" ); } if ( glue == null ) { throw new IllegalArgumentException( "No glue ; use empty String instead of null" ); } StringBuilder sb = new StringBuilder(); for ( int i = 0; i < strings.length; i++ ) { sb.append( strings[i].toString() ); // if not last if ( i < strings.length - 1 && glue.length() > 0 ) { sb.append( glue ); } } return sb.toString(); } public static String defaultJoints = " _-"; /** * Transfom joints in capital characters Chateau De Versailles => * ChateauDeVersaille white_house => whiteHouse (House is now capitalized) * groovyStyle_grails => groovyStyleGrails (inside capitals are untouched) * One difference with Java style is that the first letter might stay a * capital * * If specifiedJoints == null, the method uses default ones : " _-", thought * this can be changed with the {@link StringUtils#defaultJoints} * * @param string * @return */ public static String normalize( String string, String specifiedJoints ) { if ( specifiedJoints == null ) { specifiedJoints = defaultJoints; } String worker = replaceAny( string, specifiedJoints, " " ); String[] array = split( worker, " " ); for ( int i = 0; i < array.length; i++ ) { String s = array[i]; if ( i > 0 ) { array[i] = simpleCapitalize( s ); } } return join( array, "" ); } }