Numbers will be equals if enough decimals are equals, without thinking about approximations.
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/*
* Copyright 2007-2011 Nicolas Zozol
*
* Licensed 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 com.robustaweb.library.commons.util;
/**
* This class is designed to simplify Numbers representation in HTML views. <p>Be aware that it's not time efficiency, not designed for complex Math</p>.
* @author Nicolas Zozol for Robusta Web ToolKit & http://www.edupassion.com - nzozol@robustaweb.com
*/
public class MathUtils {
/**
* Numbers will be equals if enough decimals are equals, without thinking about approximations. This is absolutely not a pure mathematic Equality.
* <p>
* approximativelyEquals(1.2352, 1.2357, 3) returns true because there is no rounding at the 3th decimal
* </p>
*
* @param n1 first numbre
* @param n2 second number
* @param decimalPrecision
* @return true if n1 is approximatively equal to n2
*/
public static boolean approximativelyEquals(Number n1, Number n2, int decimalPrecision) {
if (n1.toString().equals(n2.toString())) {
return true;
}
String s1 = n1.toString();
String s2 = n2.toString();
s1 = StringUtils.removeTrailingCharacters(s1, '0');
s2 = StringUtils.removeTrailingCharacters(s2, '0');
if (!s1.contains(".") && !s2.contains(".")) {
return s1.equals(s2);
}
int seriousNumber;
if (s1.contains(".")) {
seriousNumber = s1.indexOf('.');
} else {//s2.contains(".")
seriousNumber = s2.indexOf('.');
}
/* checking before */
if (s1.substring(0, seriousNumber).equals(s2.substring(0, seriousNumber))) {
s1 = s1.substring(seriousNumber);
s1 = StringUtils.removeCharacter(s1, '.');
s2 = s2.substring(seriousNumber);
s2 = StringUtils.removeCharacter(s2, '.');
/* truncation */
String t1 = StringUtils.truncate(s1, decimalPrecision);
String t2 = StringUtils.truncate(s2, decimalPrecision);
t1 = StringUtils.removeTrailingCharacters(t1, '0');
t2 = StringUtils.removeTrailingCharacters(t2, '0');
return t1.equals(t2);
} else {
return false;
}
}
/**
* <p>
* Returns a visual approximation of a number, keeping only a specified decimals. <br/>
* For exemple : <br/>
* approximate(12.2578887 , 2) will return 12.25 <br/>
* approximate(12.25 , 0) will return 12 <br/>
* approximate(12.00 , 3) will return 12 <br/>
* approximate(19.5 , 0) will return 20 <br/>
*</p>
* <p>The last exemple emphasis the fact that it's made for showing convenient numbers for no mathematical public. If the number was</p>
* <p>Note that Math.round(19.5) returns 20, not 19. This function will act the same way.</p>
* @param n
* @param precision
* @return
*/
public static String approximate(Number n, int precision) {
if (n instanceof Integer) {
return n.toString();
}
String s = n.toString();
if (!s.contains(".")) {
return s;
}
String serious = s.substring(0, s.indexOf('.'));
s = s.substring(s.indexOf('.') + 1);
s = StringUtils.removeTrailingCharacters(s, '0');
if (s.length() == 0) {
return serious;
}
// We'll comments results based on approximate(12.63645, 3) and approximate(12.63545, 0)
String decimals = "";
if (s.length() > precision) {
decimals = StringUtils.truncate(s, precision + 1);
//decimal is now "636" or "6"
Float after = new Float(decimals);
//after is 636 or 6, as Float objects
after = after / 10;
//after is 63.6 or .6, as Float objects
Integer round = Math.round(after);
//round is 64 or 1
decimals = round.toString();
decimals = StringUtils.removeTrailingCharacters(decimals, '0');
} else {
decimals = StringUtils.truncate(s, precision);
decimals = StringUtils.removeTrailingCharacters(decimals, '0');
}
if (decimals.length() > 0 && precision > 0) {
return serious + "." + decimals;
} else {
//if we must round the serious string
if (decimals.length() > 0 && precision == 0) {
assert decimals.length() == 1 : "problem here !";
if (decimals.length() != 1) {
throw new IllegalStateException("Error in the algorithm for MathUtilisties.approximate(" + n + ", " + precision + ")");
}
Integer finalValue = new Integer(decimals);
if (finalValue > 0) {
Integer si = new Integer(serious);
si += 1;
return si.toString();
} else {
return serious;
}
} else {
return serious;
}
}
}
/**
* Convert a String in Double, Float, Integer, Long, Short or Byte, depending on the T exemple
* For exemple convert("2", 0f) will return 2.0 as a float
* @param <T> type returned
* @param str String to convert
* @param exemple exemple of the type
* @return a number representation of the String
* @throws IllegalArgumentException if the T type is not Double, Float, Integer, Long, Short or Byte
* @throws NumberFormatException if the conversion fails
*/
public static <T extends Number> T convert(String str, T exemple) throws NumberFormatException, IllegalArgumentException{
T result = null;
if (exemple instanceof Double) {
result = (T) new Double(str);
} else if (exemple instanceof Float) {
result = (T) new Float(str);
} else if (exemple instanceof Integer) {
result = (T) new Integer(str);
} else if (exemple instanceof Long) {
result = (T) new Long(str);
} else if (exemple instanceof Short) {
result = (T) new Short(str);
} else if (exemple instanceof Byte) {
result = (T) new Byte(str);
}else{
throw new IllegalArgumentException("Conversion is not possible with class "+exemple.getClass()
+"; only allowing Double, Float, integer, Long, Short & Byte");
}
return result;
}
}
/**
* 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#replaceAll(String, String, String) replaceAll()}
* <li> {@link StringUtils#removeCharacters(String, String) removeCharacters()}
* </ul>
* </p>
* @author Nicolas Zozol for Robusta Web ToolKit & http://www.edupassion.com - nzozol@robustaweb.com
* TODO : verify this class is GWT compatible
*/
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])+");
}
/**
* <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 originalString
* @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 originalString 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 IllegalArgumentExceptionif 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 originalString
* @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());
}
/**
* Return a String that starts with an Uppercase character, and ends with LowerCase characters
* strictCapitalize("johnDoe") returns Johndoe
* @param string 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 string 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 join
* @return
*/
public static String join (Object [] strings, String joint){
if (strings == null){
throw new IllegalArgumentException("strings array is null");
}
if (joint == null){
throw new IllegalArgumentException("No joint ; 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 && joint.length()>0){
sb.append(joint);
}
}
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, "");
}
}
Related examples in the same category